diff --git a/ROADMAP.md b/ROADMAP.md index 29da797f0..23a0c337f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -37,37 +37,37 @@ | feature | cpp | c | rust | go | cs | java | js | dart | elixir | swift | ts | lua | clojure | python | ruby | kotlin | gdscript | zig | odin | haxe | |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| -| fixed class on the table wire (its previous form; the current id-table form is its own row below) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| fixed class on the table wire (wire-form coverage below) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | text form, fixed class | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | reflection descriptors | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | block form, read side | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | cook open | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | build version | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| variable class (pointers, the flat node table) | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| text form, variable class | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| variable class (pointers, the flat node table) | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| text form, variable class | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | block form, build side | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| cook write in the runtime | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| wide scalars (128-bit) on the table wire | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| unions whose arms are tables | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| arrays of pointers | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| arrays of unions | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| optional arrays | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| blobs (*bytes, *string) | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| the wire fuzzer gate | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| maps | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| union arms of any field type | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| string, bytes and flags defaults, and renaming a table | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| renaming variants, arms and type fields | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| the 64-bit id-table wire, enum and escape kinds | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| JSON in and out of one table instance | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| wstring(N) on the table wire (#522) | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| unbounded arrays | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| the message form | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| bitpacked table message form | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| retain-unknown | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| cook write in the runtime | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| wide scalars (128-bit) on the table wire | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| unions whose arms are tables | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| arrays of pointers | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| arrays of unions | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| optional arrays | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| blobs (*bytes, *string) | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| the wire fuzzer gate | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| maps | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| union arms of any field type | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| string, bytes and flags defaults, and renaming a table | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| renaming variants, arms and type fields | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| the 64-bit id-table wire, enum and escape kinds | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| JSON in and out of one table instance | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| wstring(N) on the table wire (#522) | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| unbounded arrays | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| the message form | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| bitpacked table message form | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| retain-unknown | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | doc comments and tags in the descriptors | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| the unit registry, UnitView | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| widening on read, and the refusal reasons | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| the unit registry, UnitView | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| widening on read, and the refusal reasons | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Go supports the complete table surface through caller-owned regions, mutable arena builders and cooks. `test-go` covers all eighteen conformance surfaces, diff --git a/compiler/issue710_test.go b/compiler/issue710_test.go index 3dd538252..e932c2424 100644 --- a/compiler/issue710_test.go +++ b/compiler/issue710_test.go @@ -167,6 +167,34 @@ func TestIssue710TableRecovery(t *testing.T) { } cpp.WriteString("return 0;}\n") issue710CompileRun(t, dir, "c++", ".cpp", cpp.String()) + cfiles, err := c.Generate(u, "c", Options{}) + if err != nil { + t.Fatal(err) + } + cdir := t.TempDir() + for name, data := range cfiles { + if err := os.WriteFile(filepath.Join(cdir, name), data, 0644); err != nil { + t.Fatal(err) + } + } + checks := []string{ + `Text v; TableReport r={0}; if(!text_load(&v,wire,sizeof(wire),&r) || !r.malformed || v.after!=42 || v.label_length!=8 || strcmp(v.label,"untitled")) return 1;`, + `Rows v; TableReport r={0}; if(!rows_load(&v,wire,sizeof(wire),&r) || !r.malformed || v.after!=42 || v.items_count!=1 || v.items[0].value!=7) return 2;`, + `MappingBuilder b; TableReport r={0}; int ok; if(!mapping_builder_init(&b))return 3; ok=mapping_load_builder(&b,wire,sizeof(wire),&r); if(!ok || !r.malformed || mapping_builder_root(&b)->after!=42 || mapping_builder_root(&b)->items.count) return 4; mapping_builder_shutdown(&b);`, + `Keyed v; TableReport r={0}; if(!keyed_load(&v,wire,sizeof(wire),&r) || !r.malformed || v.after!=42 || v.items[0].value!=7 || v.items[1].value!=55) return 5;`, + } + var csrc strings.Builder + csrc.WriteString("#include \"ProbeTable.h\"\nint main(void){\n") + for i, tc := range cases { + csrc.WriteString("{const uint8_t wire[]={") + for _, b := range tc.wire { + fmt.Fprintf(&csrc, "%d,", b) + } + csrc.WriteString("};" + checks[i] + "}\n") + } + csrc.WriteString("return 0;}\n") + issue710CompileRun(t, cdir, "cc", ".c", csrc.String()) + } func issue710CompileRun(t *testing.T, dir, compiler, ext, source string) { @@ -338,6 +366,32 @@ func TestIssue710ListRefusal(t *testing.T) { } cpp.WriteString("return 0;}\n") issue710CompileRun(t, dir, "c++", ".cpp", cpp.String()) + cfiles, err := New().Generate(u, "c", Options{}) + if err != nil { + t.Fatal(err) + } + cdir := t.TempDir() + for name, data := range cfiles { + if err := os.WriteFile(filepath.Join(cdir, name), data, 0644); err != nil { + t.Fatal(err) + } + } + var csrc strings.Builder + csrc.WriteString("#include \"ProbeTable.h\"\nint main(void){\n") + for _, tc := range []struct { + root, fn string + body []byte + }{{"Lists", "lists", body}, {"Nested", "nested", nested}, {"KeyedLists", "keyed_lists", keyed}} { + wire := issue710Wire(tc.body, ids...) + csrc.WriteString("{const uint8_t wire[]={") + for _, b := range wire { + fmt.Fprintf(&csrc, "%d,", b) + } + fmt.Fprintf(&csrc, "};%sBuilder b;TableReport r={0};int reason=0;if(!%s_builder_init(&b))return 1;if(%s_load_builder(&b,wire,sizeof(wire),&r) || r.malformed || r.unknown || r.kind_mismatch || %s_builder_root(&b)->after) return 2;if(%s_load_measure_ex(wire,sizeof(wire),NULL,&reason)!=-1 || reason!=11)return 3;%s_builder_shutdown(&b); }\n", tc.root, tc.fn, tc.fn, tc.fn, tc.fn, tc.fn) + } + csrc.WriteString("return 0;}\n") + issue710CompileRun(t, cdir, "cc", ".c", csrc.String()) + } func TestIssue710CTableBounds(t *testing.T) { diff --git a/compiler/packetvaluedefaults_test.go b/compiler/packetvaluedefaults_test.go index 7788c724b..60e22e7ad 100644 --- a/compiler/packetvaluedefaults_test.go +++ b/compiler/packetvaluedefaults_test.go @@ -63,7 +63,7 @@ func TestPacketValueDefaultsCarriers(t *testing.T) { func TestPacketValueDefaultsBesideUnrelatedTable(t *testing.T) { u := unitFromSource(t, packetValueDefaultsUnit+"\ntable Counter { number int32 }\n") - for _, target := range []string{"c", "go", "rust", "java", "js", "dart", "elixir"} { + for _, target := range []string{"rust", "java", "js", "dart", "elixir"} { if _, err := New().Generate(u, target, Options{}); err != nil { t.Fatalf("%s: an unrelated table must not turn packet defaults into table defaults: %v", target, err) } @@ -101,7 +101,7 @@ type Loose src := "package vdef\nflags Caps { Jump, Crouch }\n" + tc.decl + " Badge {\n" + fields + "}\n" + tc.edge + packet u := unitFromSource(t, src) - for _, target := range []string{"c", "rust", "java", "js", "dart", "elixir"} { + for _, target := range []string{"rust", "java", "js", "dart", "elixir"} { _, err := New().Generate(u, target, Options{}) if err == nil { t.Fatal("table-closure defaults accepted without table reset and elision support") @@ -113,7 +113,7 @@ type Loose t.Errorf("refusal does not name %q: %v", want, err) } } - if strings.Contains(err.Error(), "Loose.label") || !strings.Contains(err.Error(), "generate with --lang cpp, --lang cs and --lang go, or drop the default") { + if strings.Contains(err.Error(), "Loose.label") || !strings.Contains(err.Error(), "generate with --lang c, --lang cpp, --lang cs and --lang go, or drop the default") { t.Errorf("table refusal includes a supported packet field or names %s as a table carrier: %v", target, err) } } diff --git a/compiler/packetvoidarms_test.go b/compiler/packetvoidarms_test.go index a98778e22..39583cc34 100644 --- a/compiler/packetvoidarms_test.go +++ b/compiler/packetvoidarms_test.go @@ -96,12 +96,12 @@ func TestScalarArmInTypeBodyIsRefusedByCheck(t *testing.T) { } } -func TestPacketVoidArmSupportPreservesCTableRefusal(t *testing.T) { +func TestPacketAndTableVoidArmsInC(t *testing.T) { u := unitFromSource(t, strings.Replace(voidArmInTypeBody, "type FireCommand", "table FireCommand", 1)) c := New() _, err := c.Generate(u, "c", Options{}) - if err == nil || !strings.Contains(err.Error(), "payload-free arm in a table closure") || !strings.Contains(err.Error(), "WeaponFire") { - t.Fatalf("C must retain its named table-closure refusal, got %v", err) + if err != nil { + t.Fatalf("C refused a supported table payload-free arm: %v", err) } if _, err := c.Generate(u, "cpp", Options{}); err != nil { t.Fatalf("C++ must retain its existing table payload-free support: %v", err) diff --git a/compiler/packetwidetext_test.go b/compiler/packetwidetext_test.go index 0d4fabcb5..59b8561df 100644 --- a/compiler/packetwidetext_test.go +++ b/compiler/packetwidetext_test.go @@ -34,6 +34,12 @@ func TestPacketWideTextRefusesTableClosure(t *testing.T) { continue } _, err := New().Generate(u, target, nil) + if target == "c" { + if err != nil { + t.Errorf("C refused table wide text: %v", err) + } + continue + } if err == nil || !strings.Contains(err.Error(), "table closure") || !strings.Contains(err.Error(), "wstring(N)") { t.Errorf("%s failed to refuse the table-wide closure: %v", target, err) } diff --git a/compiler/repeatedarraytail_test.go b/compiler/repeatedarraytail_test.go index 825f13ecb..2fcf7712a 100644 --- a/compiler/repeatedarraytail_test.go +++ b/compiler/repeatedarraytail_test.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "fmt" "path/filepath" + "strings" "testing" ) @@ -29,6 +30,16 @@ func TestRepeatedArrayTailDefaults(t *testing.T) { #define CHECK(x) do{if(!(x)){fprintf(stderr,"tail defaults line %%d: %%s\n",__LINE__,#x);return 1;}}while(0) static const uint8_t wire[]={%s}; `, cppWire(wire)) + t.Run("c-bounded-extent", func(t *testing.T) { + // Replace the first array count with UINT64_MAX, preserving its L. + // Only the two encoded elements exist; extent must stop at EOF. + mutant := append([]byte(nil), wire[:5]...) + mutant[3] += 9 + mutant = append(mutant, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1) + mutant = append(mutant, wire[6:]...) + source := strings.Replace(common, cppWire(wire), cppWire(mutant), 1) + runCTableWireProbe(t, u, source+`int main(void){CHECK(hand_load_measure(wire,sizeof(wire))==88);return 0;}`) + }) t.Run("cpp", func(t *testing.T) { files, err := New().Generate(u, "cpp", Options{}) if err != nil { @@ -37,4 +48,8 @@ static const uint8_t wire[]={%s}; referenceCompileRun(t, files, common+`using namespace armdemo; int main(){TableReport report;int64_t n=HandLoadMeasure(wire,sizeof(wire));CHECK(n>0);uint8_t * region=(uint8_t*)malloc(n);const Hand * r=HandLoad(region,n,wire,sizeof(wire),&report);CHECK(r);CHECK(report.malformed);CHECK(r->entries_count==0);CHECK(r->entries[1].type==CarryType::None);HandBuilder b;CHECK(HandLoadBuilder(b,wire,sizeof(wire),&report));CHECK(b.GetRoot()->entries[1].type==CarryType::None);CHECK(HandCookMeasure(b)>0);free(region);return 0;}`) }) + t.Run("c", func(t *testing.T) { + runCTableWireProbe(t, u, common+` +int main(void){TableReport report={0};int64_t n=hand_load_measure(wire,sizeof(wire));CHECK(n>0);uint8_t * region=(uint8_t*)malloc((size_t)n);const Hand * r=hand_load(region,n,wire,sizeof(wire),&report);CHECK(r);CHECK(report.malformed);CHECK(r->entries_count==0);CHECK(r->entries[1].type==0);free(region);return 0;}`) + }) } diff --git a/compiler/tablesc_test.go b/compiler/tablesc_test.go index f5206bd12..cfa37ca15 100644 --- a/compiler/tablesc_test.go +++ b/compiler/tablesc_test.go @@ -35,7 +35,15 @@ func TestCTableRuntimeNamesAreClaimed(t *testing.T) { if err != nil { t.Fatal(err) } - ident := regexp.MustCompile(`\b(?:Table|kTable|table_|BuildVersion)[A-Za-z0-9_]*\b`) + // Include a fixed unit to check the allocation-free runtime as well. + fixed, err := New().Generate(unitFromSource(t, runtimeSrc), "c", Options{}) + if err != nil { + t.Fatal(err) + } + for name, data := range fixed { + files["fixed-"+name] = data + } + ident := regexp.MustCompile(`\b(?:(?:Table|kTable|table_|BuildVersion|schema_allocate|schema_release|schema_assert|schema_fatal)[A-Za-z0-9_]*|announce(?:_measure|_read)?)\b`) // the unit's own type names start with Table for a schema that declares one; // the corpus here declares none, and the file base does, so the two file // spellings the include lines carry are excluded by name rather than by a @@ -151,14 +159,20 @@ func TestCExternalsCarryThePackage(t *testing.T) { // must not become a collision the day a table gains a pointer (§11) — so the // scan needs a corpus where every name is actually emitted. const cRuntimeSrc = runtimeSrc + ` +table ScalarLeaf { value uint32 } table Node { value int32 next *Node } +table Collections { items []Node + rows map[string(8)]Node +} + ` -// TestCForceInlineStopsAtTheVariableClass is the C twin of the reference's +// TestCForceInlineStopsAtTheVariableClass also leaves composite bodies plain +// to keep deep by-value closures from expanding during compilation. It is the C twin of the reference's // recursion guard (schema#343). The force-inline qualifier carries the fixed // class's bodies and stops there, because that is the class whose save/load // call graph cannot hold a cycle: a fixed table nests by value, and a by-value @@ -179,8 +193,8 @@ func TestCForceInlineStopsAtTheVariableClass(t *testing.T) { t.Fatal("no ProbeTable.h") } for _, want := range []string{ - "static SCHEMA_UNUSED SCHEMA_PROBE_TABLE_INLINE int config_save_body( TableWriter * w, const Config * value )", - "static SCHEMA_UNUSED SCHEMA_PROBE_TABLE_INLINE int config_load_body( TableReader * r, Config * value )", + "static SCHEMA_UNUSED SCHEMA_PROBE_TABLE_INLINE int scalar_leaf_save_body( TableWriter * w, const ScalarLeaf * value )", + "static SCHEMA_UNUSED SCHEMA_PROBE_TABLE_INLINE int scalar_leaf_load_body( TableReader * r, ScalarLeaf * value )", "static SCHEMA_UNUSED SCHEMA_PROBE_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v )", "static SCHEMA_UNUSED SCHEMA_PROBE_TABLE_INLINE uint32_t table_reader_get32( TableReader * r )", } { @@ -277,8 +291,8 @@ func TestCGeneratorMacrosAreOwned(t *testing.T) { continue } // anything carrying the schema's own prefix is a macro the - // DECLARATIONS asked for, in either spelling the emitter uses - if strings.Contains(m[1], "ZQQ") || strings.Contains(m[1], "Zqq") { + // DECLARATIONS asked for, in the upper, Pascal and C snake spellings the emitter uses + if strings.Contains(m[1], "ZQQ") || strings.Contains(m[1], "Zqq") || strings.HasPrefix(m[1], "zqq_") { continue } owned[m[1]] = true diff --git a/compiler/tablesccook_test.go b/compiler/tablesccook_test.go new file mode 100644 index 000000000..b68d14efd --- /dev/null +++ b/compiler/tablesccook_test.go @@ -0,0 +1,206 @@ +package compiler + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/mas-bandwidth/schema/v2/internal/tabletext" + "github.com/mas-bandwidth/schema/v2/internal/tablewire" +) + +// The compiler's canonical writer is independent of the C generator. These +// bytes include compact, differently aligned graph nodes and nested extents. +func TestCTableCookCanonical(t *testing.T) { + u := unitFromSource(t, `package probe +table Tiny { value uint8 } +table Bucket { numbers []int16 } +table Root { + flag uint8 + first *Tiny + second *Tiny + text string(8) + values [..2]uint8 + buckets [..2]Bucket + hidden [..2]*Tiny +} +`) + m := tabletext.NewModel(u) + value := m.New(u.Tables["Root"]) + report := tabletext.Report{} + if !m.Read(value, []byte(`{"flag":7,"first":{"value":3},"second":{"value":5},"text":"hi","values":[8,9],"buckets":[{"numbers":[1,-2,3]}]}`), &report) || !report.Silent() { + t.Fatalf("input: %+v", report) + } + wire, err := tablewire.Encode(m, value) + if err != nil { + t.Fatal(err) + } + octets := func(b []byte) string { + var s strings.Builder + for _, v := range b { + fmt.Fprintf(&s, "0x%02x,", v) + } + return s.String() + } + var arrays strings.Builder + fmt.Fprintf(&arrays, "static const uint8_t wire[]={%s};\n", octets(wire)) + for _, big := range []bool{false, true} { + cooked, _, _, err := New().Cook(u, "Root", wire, CookOptions{Big: big}) + if err != nil { + t.Fatal(err) + } + name := "little" + if big { + name = "big" + } + fmt.Fprintf(&arrays, "static const uint8_t %s[]={%s};\n", name, octets(cooked)) + } + files, err := New().Generate(u, "c", Options{}) + if err != nil { + t.Fatal(err) + } + dir := t.TempDir() + for name, data := range files { + if err := os.WriteFile(filepath.Join(dir, name), data, 0600); err != nil { + t.Fatal(err) + } + } + source := `#include "ProbeTable.h" +#include +#define CHECK(x) do { if(!(x)) { fprintf(stderr,"line %d: %s\n",__LINE__,#x); return 1; } } while(0) +` + arrays.String() + ` +int main(void) { + RootBuilder b; Root * root; TableCtx ctx; TableSink sink; TableReport report={0}; + uint8_t output[sizeof(little)+1],packed[4096]; int64_t need; int i; int16_t * number; + const Root * opened; const Tiny * first; + { TableRefuseReason reason=99; uint8_t unknown=2; + CHECK(root_load_measure_ex(wire,sizeof(wire),NULL,&reason)>0 && reason==99); + CHECK(root_load_measure_ex(&unknown,1,NULL,&reason)==-1 && reason==SCHEMA_TABLE_REFUSE_UNKNOWN_FORM); + reason=99;CHECK(root_load_measure_ex(NULL,0,NULL,&reason)==-1 && reason==99); + } + CHECK(root_builder_init(&b));CHECK(root_load_builder(&b,wire,sizeof(wire),&report)); + root=root_builder_root(&b);ctx.arena=&b.arena;sink.worker=&b.main;sink.region=NULL; + /* Padding and unused string tails must not enter a content-addressed cook. */ + ((uint8_t *)(void *)root)[1]=0xa5; + for(i=root->text_length+1;i<(int)sizeof(root->text);i++) {root->text[i]=(char)0xa5;} + need=root_cook_measure(&ctx,root);CHECK(need==(int64_t)sizeof(little)); + memset(output,0xa5,sizeof(output));CHECK(!root_cook(&ctx,root,output,need-1,TableByteOrder_Little)); + for(i=0;i<(int)sizeof(output);i++) {CHECK(output[i]==0xa5);} + CHECK(root_cook(&ctx,root,output,need,TableByteOrder_Little));CHECK(!memcmp(output,little,sizeof(little)));CHECK(output[need]==0xa5); + CHECK(root_cook(&ctx,root,output,need,TableByteOrder_Big));CHECK(!memcmp(output,big,sizeof(big))); + CHECK(root_cook(&ctx,root,output,need,table_cook_byte_order==1 ? TableByteOrder_Little : TableByteOrder_Big)); + opened=root_open(output,need);CHECK(opened!=NULL);first=tiny_at(NULL,&opened->first);CHECK(first!=NULL && first->value==3); + CHECK(root_save(NULL,opened,packed,sizeof(packed))==(int64_t)sizeof(wire));CHECK(!memcmp(packed,wire,sizeof(wire))); + /* An unreached list must refuse before touching the output. */ + number=bucket_numbers_add(&b.main,&root->buckets[1].numbers);CHECK(number!=NULL);*number=42; + CHECK(root_cook_measure(&ctx,root)==-1);memset(output,0xa5,sizeof(output)); + CHECK(!root_cook(&ctx,root,output,sizeof(output),TableByteOrder_Little));CHECK(output[0]==0xa5);CHECK(!root_builder_lock(&b)); + CHECK(bucket_numbers_erase(&b.arena,&root->buckets[1].numbers,number)); + CHECK(root_cook_measure(&ctx,root)==need); + /* A hidden pointer is patched only if its node was independently reached. */ + CHECK(tiny_emplace(&sink,&root->hidden[1])!=NULL); + CHECK(root_cook_measure(&ctx,root)==need);CHECK(!root_cook(&ctx,root,output,sizeof(output),TableByteOrder_Little)); + CHECK(root_open(output,need)==NULL);CHECK(!root_builder_lock(&b)); + root->hidden[1]=root->first; + CHECK(root_cook(&ctx,root,output,sizeof(output),table_cook_byte_order==1 ? TableByteOrder_Little : TableByteOrder_Big)); + opened=root_open(output,need);CHECK(opened!=NULL && tiny_at(NULL,&opened->hidden[1])==tiny_at(NULL,&opened->first)); + CHECK(root_builder_lock(&b));opened=(const Root *)(const void *)b.region; + CHECK(tiny_at(NULL,&opened->hidden[1])==tiny_at(NULL,&opened->first)); + root_builder_shutdown(&b);return 0; +} +` + issue710CompileRun(t, dir, "cc", ".c", source) +} + +// Refusals name the first failed clause. No counter or success path mutates +// the caller's reason, and a const block never exposes writable rows. +func TestCTableCookAndBlockRefusals(t *testing.T) { + u := unitFromSource(t, `package probe +enum Key { One, Two } +type Row { value int32 } +table Root { + rows [..2]Row + keys [Key]int32 +} +`) + files, err := New().Generate(u, "c", Options{}) + if err != nil { + t.Fatal(err) + } + dir := t.TempDir() + for name, data := range files { + if err := os.WriteFile(filepath.Join(dir, name), data, 0600); err != nil { + t.Fatal(err) + } + } + source := `#include +#include +#include +static int allocations,releases; +static jmp_buf fatal; +static void * allocate_hook(int64_t n) { allocations++; return calloc(1,(size_t)n); } +static void release_hook(void * p) { if(p!=NULL) {releases++;} free(p); } +#define schema_allocate(n) allocate_hook(n) +#define schema_release(p) release_hook(p) +#define schema_assert(x) ((void)(x)) +#define schema_fatal() longjmp(fatal,1) +#include "ProbeBlock.c" +#include +#define CHECK(x) do { if(!(x)) { fprintf(stderr,"line %d: %s\n",__LINE__,#x); return 1; } } while(0) +int main(void) { + uint8_t space[2048], skewed[2048]; + uint8_t * output=(uint8_t *)(((uintptr_t)space+63)&~(uintptr_t)63); + uint8_t * skew=(uint8_t *)(((uintptr_t)skewed+63)&~(uintptr_t)63)+1; + Root root; TableRefuseReason reason=99; int64_t need; int i; + RootBlockStorage storage; RootBlock block; RootBlockConst read_only; RootCounts counts={0}; TableBlockAllocator pair=table_block_default_allocator(); + TableByteOrder order=table_cook_byte_order==1 ? TableByteOrder_Little : TableByteOrder_Big; + static const int offsets[]={0,8,16,24,32,40,48,56}; + static const int reasons[]={SCHEMA_TABLE_REFUSE_NOT_A_COOK,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION,SCHEMA_TABLE_REFUSE_NOT_A_COOK,SCHEMA_TABLE_REFUSE_TRUNCATED,SCHEMA_TABLE_REFUSE_TRUNCATED,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO}; + root_reset(&root);need=root_cook_measure(&root); + CHECK(root_cook(&root,output,need,order));CHECK(root_open_ex(output,need,&reason)!=NULL && reason==99); + CHECK(root_open_ex(NULL,need,&reason)==NULL && reason==SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); + CHECK(root_open_ex(output,63,&reason)==NULL && reason==SCHEMA_TABLE_REFUSE_TRUNCATED); + for(i=0;i<8;i++) { + CHECK(root_cook(&root,output,need,order));table_cook_put(output+offsets[i],3,8,order); + CHECK(root_open_ex(output,need,&reason)==NULL && reason==reasons[i]); + } + CHECK(root_cook(&root,output,need,order==TableByteOrder_Little ? TableByteOrder_Big : TableByteOrder_Little)); + CHECK(root_open_ex(output,need,&reason)==NULL && reason==SCHEMA_TABLE_REFUSE_FOREIGN_ORDER); + CHECK(root_cook(&root,output,need,order));memcpy(skew,output,(size_t)need); + CHECK(root_open_ex(skew,need,&reason)==NULL && reason==SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); + table_cook_put(skew,3,8,order);CHECK(root_open_ex(skew,need,&reason)==NULL && reason==SCHEMA_TABLE_REFUSE_NOT_A_COOK); + CHECK(root_block_storage_create(&storage,&pair));CHECK(allocations==1 && releases==0); + counts.rows=2;CHECK(root_block_begin(&block,&storage,&counts,NULL));root_rows_span(&block)[1].value=17; + need=root_block_bytes(&block);reason=99; + CHECK(root_block_open_const_ex(&read_only,block.base,need,&reason));CHECK(reason==99 && root_block_bytes_const(&read_only)==need); + CHECK(root_rows_span_const(&read_only)[1].value==17); + { TableBlockConstRows rows=root_rows_rows_const(&read_only); CHECK(((const Row *)table_block_row_at_const(&rows,1))->value==17); } + memcpy(skew,block.base,(size_t)need);CHECK(!root_block_open_const_ex(&read_only,skew,need,&reason));CHECK(reason==SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); + /* The gate must read an unaligned triple bytewise before refusing the base. */ + table_cook_put(skew+offsetof(RootBlockProjection,rows)+12,99,4,order); + CHECK(!root_block_open_const_ex(&read_only,skew,need,&reason));CHECK(reason==SCHEMA_TABLE_REFUSE_BAD_LAYOUT); + table_cook_put(skew,3,8,order);CHECK(!root_block_open_const_ex(&read_only,skew,need,&reason));CHECK(reason==SCHEMA_TABLE_REFUSE_NOT_A_COOK); + CHECK(read_only.base==NULL && read_only.projection==NULL && read_only.bytes==0); + CHECK(!root_block_open_ex(&block,NULL,0,&reason));CHECK(reason==SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); + root_block_storage_destroy(&storage);CHECK(allocations==1 && releases==1); + if(setjmp(fatal)==0) { SCHEMA_TABLE_KEYED_AT(root.keys,0,sizeof(root.keys)/sizeof(root.keys[0]))=1; return 1; } + return 0; +} +` + issue710CompileRun(t, dir, "cc", ".c", source) + bad := `#include "ProbeBlock.h" +void mutate(const RootBlockConst * block) { root_rows_span_const(block)[0].value=9; } +` + path := filepath.Join(dir, "const-negative.c") + if err := os.WriteFile(path, []byte(bad), 0600); err != nil { + t.Fatal(err) + } + output, err := exec.Command("cc", "-std=c99", "-I", dir, "-c", path, "-o", filepath.Join(dir, "const-negative.o")).CombinedOutput() + if err == nil || (!strings.Contains(string(output), "const") && !strings.Contains(string(output), "read-only")) { + t.Fatalf("const block write did not fail for constness: %v\n%s", err, output) + } + +} diff --git a/compiler/tablescmessage_test.go b/compiler/tablescmessage_test.go new file mode 100644 index 000000000..34d3a6803 --- /dev/null +++ b/compiler/tablescmessage_test.go @@ -0,0 +1,287 @@ +package compiler + +import ( + "fmt" + "strings" + "testing" + + "github.com/mas-bandwidth/schema/v2/internal/tabletext" + "github.com/mas-bandwidth/schema/v2/internal/tablewire" + "github.com/mas-bandwidth/schema/v2/ir" +) + +const cMessageSchema = `package probe +enum Choice { First, Second } +flags Features { One, Two, Three } +type Leaf { x int32 = -17 | min = -1000, max = 1000 +choice Choice } +union Body { text string(16) +numbers [..3]int16 +leaf Leaf +empty } +table Root { + enabled bool + amount int16 = -3 | min = -8, max = 47 + ratio float32 | min = -1, max = 1, resolution = 0.01 + mask Features + name string(12) + payload bytes(12) + units wstring(12) + leaf Leaf + counters [2]uint32 + slots [Choice]Leaf + samples [..3]int32 | min = 0, max = 0 + body Body + history [..2]Body + choices ?[2]Choice +} +` + +func TestCTableMessageSave(t *testing.T) { + u := unitFromSource(t, cMessageSchema) + m := tabletext.NewModel(u) + texts := []string{`{}`, `{"enabled":true,"amount":17,"ratio":0.42,"mask":["One","Three"],"name":"hi","payload":"AQID","units":"𝄞","leaf":{"x":9,"choice":"Second"},"counters":[4,5],"slots":{"First":{"x":5}},"samples":[0,0,0],"body":{"text":"hello"},"history":[{"numbers":[2,3]},{"empty":null}],"choices":["First","Second"]}`, `{"body":{"leaf":{"x":22}},"payload":"BAUGBw=="}`} + var instances []*tabletext.Instance + var setup strings.Builder + for i, s := range texts { + v := m.New(m.Lookup("Root")) + var report tabletext.Report + if !m.Read(v, []byte(s), &report) || !report.Silent() { + t.Fatalf("fixture %d: %+v", i, report) + } + instances = append(instances, v) + fmt.Fprintf(&setup, "CHECK(root_from_json(values+%d,%q,%d,&report));\n", i, s, len(s)) + } + expected, err := tablewire.EncodeMessages(m, instances) + if err != nil { + t.Fatal(err) + } + octets := func(b []byte) string { + var s strings.Builder + for _, v := range b { + fmt.Fprintf(&s, "0x%02x,", v) + } + return s.String() + } + source := fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do {if(!(x)){fprintf(stderr,"line %%d: %%s\n",__LINE__,#x);return 1;}} while(0) +static const uint8_t expected[]={%s}; +static const uint8_t announcement[]={%s}; +int main(void) { + Root values[3],decoded[3]; TableReport report={0}; TableMessageEntry entries[kTableMessageEntriesHere]; + TableVocabulary vocabulary=table_vocabulary(entries,kTableMessageEntriesHere);uint8_t output[4096];int64_t n,count; + %s + CHECK(!report.malformed && !report.clamped && !report.unknown); + CHECK(announce_measure()==sizeof(announcement));CHECK(announce(output,sizeof(output))==sizeof(announcement));CHECK(!memcmp(output,announcement,sizeof(announcement))); + CHECK(announce_read(&vocabulary,announcement,sizeof(announcement),&report));CHECK(vocabulary.announced && vocabulary.count==kTableMessageEntriesHere); + CHECK(!report.refused && !report.malformed); + n=root_measure_messages(values,3,&report);CHECK(n==sizeof(expected)); + CHECK(root_save_messages(values,3,output,n,&report)==n);CHECK(!memcmp(output,expected,sizeof(expected))); + count=3;CHECK(root_load_messages(decoded,&count,&vocabulary,expected,sizeof(expected),&report));CHECK(count==3); + CHECK(!report.malformed&&!report.refused&&!report.unknown&&!report.clamped&&!report.widened&&!report.kind_mismatch); + CHECK(root_save_messages(decoded,3,output,n,&report)==n);CHECK(!memcmp(output,expected,sizeof(expected))); + output[n-1]=0xa5;CHECK(root_save_messages(values,3,output,n-1,&report)==-1);CHECK(output[n-1]==0xa5); + CHECK(root_measure_messages(values,0,&report)==-1);CHECK(root_measure_messages(values,257,&report)==-1); + CHECK(report.refused && report.reason==SCHEMA_TABLE_BATCH_TOO_LARGE && !report.malformed); + return 0; +}`, octets(expected), octets(tablewire.Announce(u)), setup.String()) + runCTableWireProbe(t, u, source) +} + +func TestCTableAnnouncementDistinctQuantizedShapes(t *testing.T) { + u := unitFromSource(t, `package probe +table First { rate float32 | min = 0, max = 1, resolution = 0.1 } +table Second { rate float32 | min = 0, max = 1, resolution = 0.1001 } +`) + var vocabulary tablewire.Vocabulary + var report tabletext.Report + if err := vocabulary.AnnounceRead(tablewire.Announce(u), &report); err != nil { + t.Fatal(err) + } + runCTableWireProbe(t, u, `#include "ProbeTable.h" +#include +int main(void) { + TableMessageEntry entries[kTableMessageEntriesHere];TableVocabulary v=table_vocabulary(entries,kTableMessageEntriesHere); + TableReport report={0};uint8_t bytes[kTableAnnounceBytes]; + if(sizeof(TableMessageEntry)!=96)return 2; + if(announce(bytes,sizeof(bytes))!=sizeof(bytes))return 1; + if(!announce_read(&v,bytes,sizeof(bytes),&report)){fprintf(stderr,"own announcement: malformed=%d refused=%d\n",report.malformed,report.refused);return 1;} + return 0; +}`) +} + +func TestCTableMessageGraph(t *testing.T) { + u := unitFromSource(t, `package probe +table Node { n int32 +next *Node +items []int16 } +table Item { samples []uint8 } +union Action { number uint32 +blob *bytes +node *Node } +table Root { node *Node +aliases [..3]*Node +blob *bytes +title *string +numbers []uint8 +zeroes []int32 | min = 0, max = 0 +entries map[int32]Item +action Action } +`) + text := `{"node":{"&node":1,"n":7,"items":[2,3]},"aliases":[{"&node":1},null],"blob":"AAEC","title":"hi","numbers":[1,2],"zeroes":[0,0],"entries":{"7":{"samples":[4,5]},"2":{"samples":[9]}},"action":{"node":{"n":11}}}` + m := tabletext.NewModel(u) + v := m.New(m.Lookup("Root")) + var report tabletext.Report + if !m.Read(v, []byte(text), &report) || !report.Silent() { + t.Fatalf("fixture: %+v", report) + } + values := []*tabletext.Instance{v, m.New(m.Lookup("Root"))} + expected, err := tablewire.EncodeMessages(m, values) + if err != nil { + t.Fatal(err) + } + var bytes strings.Builder + for _, v := range expected { + fmt.Fprintf(&bytes, "0x%02x,", v) + } + source := fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do {if(!(x)){fprintf(stderr,"line %%d: %%s\n",__LINE__,#x);return 1;}} while(0) +static const uint8_t expected[]={%s}; +int main(void){ + RootBuilder a,b;const Root * roots[2],*loaded[2];uint8_t buffer[4096],*region;int64_t n,storage,count,attribution; + TableMessageEntry entries[kTableMessageEntriesHere];TableVocabulary vocabulary=table_vocabulary(entries,kTableMessageEntriesHere);TableReport report={0};TableRefuseReason reason=99; + CHECK(root_builder_init(&a));CHECK(root_builder_init(&b));CHECK(root_from_json(&a,%q,%d,&report));CHECK(!report.malformed&&!report.clamped&&!report.unknown); + CHECK(root_builder_root(&b)!=NULL);CHECK(root_builder_lock(&a));CHECK(root_builder_lock(&b));roots[0]=(const Root *)(const void *)a.region;roots[1]=(const Root *)(const void *)b.region; + n=root_measure_messages(roots,2,&report);CHECK(n==sizeof(expected));CHECK(root_save_messages(roots,2,buffer,n,&report)==n);CHECK(!memcmp(buffer,expected,sizeof(expected))); + CHECK(announce_read(&vocabulary,kTableAnnounce,kTableAnnounceBytes,&report)); + storage=root_load_measure_messages_ex(&vocabulary,expected,sizeof(expected),&attribution,&reason);CHECK(storage>attribution&&reason==99); + region=(uint8_t *)malloc((size_t)storage+8);CHECK(region!=NULL);memset(region,0xa5,(size_t)storage+8); + count=2;CHECK(root_load_messages(loaded,&count,region,storage,&vocabulary,expected,sizeof(expected),&report));CHECK(count==2&&!report.malformed&&!report.refused&&!report.unknown&&!report.clamped&&!report.kind_mismatch); + CHECK(region[storage]==0xa5);CHECK(node_at(NULL,&loaded[0]->node)==node_at(NULL,&loaded[0]->aliases[0])); + CHECK(root_save_messages(loaded,2,buffer,n,&report)==n);CHECK(!memcmp(buffer,expected,sizeof(expected))); + count=1;CHECK(!root_load_messages(loaded,&count,region,storage,&vocabulary,expected,sizeof(expected),&report));CHECK(count==2&&report.reason==SCHEMA_TABLE_BATCH_TOO_LARGE); + root_builder_shutdown(&a);root_builder_shutdown(&b);free(region);return 0; +}`, bytes.String(), text, len(text)) + runCTableWireProbe(t, u, source) +} + +func TestCTableMessageEvolution(t *testing.T) { + sender := unitFromSource(t, `package probe +type Item { value int8 } +union Payload { number uint8 } +table Root { x int8 +y uint8 +actual float32 +name string(16) +values [..4]uint8 +child Item +choice Payload +extra uint32 +changed bool } +`) + receiver := unitFromSource(t, `package probe +type Item { value int32 } +union Payload { number uint32 } +table Root { x int32 = 1000 | min=1000,max=2000 +y uint64 | min=0,max=100 +actual float64 +name string(4) +values [..2]uint32 | min=0,max=100 +child Item +choice Payload +changed string(4) } +`) + sm, rm := tabletext.NewModel(sender), tabletext.NewModel(receiver) + v := sm.New(sm.Lookup("Root")) + var report tabletext.Report + text := []byte(`{"x":-3,"y":200,"actual":1.25,"name":"ééé","values":[4,150,20],"child":{"value":-5},"choice":{"number":255},"extra":7,"changed":true}`) + if !sm.Read(v, text, &report) || !report.Silent() { + t.Fatalf("fixture: %+v", report) + } + wire, err := tablewire.EncodeMessage(sm, v) + if err != nil { + t.Fatal(err) + } + announcement := tablewire.Announce(sender) + var vocabulary tablewire.Vocabulary + if err := vocabulary.AnnounceRead(announcement, &report); err != nil { + t.Fatal(err) + } + decoded := rm.New(rm.Lookup("Root")) + if ok, err := tablewire.DecodeMessage(rm, decoded, wire, &vocabulary, &report); err != nil || !ok { + t.Fatalf("oracle: %v %+v", err, report) + } + expected, err := tablewire.Encode(rm, decoded) + if err != nil { + t.Fatal(err) + } + octets := func(b []byte) string { + var out strings.Builder + for _, v := range b { + fmt.Fprintf(&out, "0x%02x,", v) + } + return out.String() + } + source := fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do{if(!(x)){fprintf(stderr,"line %%d: %%s\n",__LINE__,#x);return 1;}}while(0) +static const uint8_t announcement[]={%s},wire[]={%s},expected[]={%s}; +int main(void){ + Root value;TableMessageEntry entries[128];TableVocabulary v=table_vocabulary(entries,128);TableReport report={0};int64_t count=1,n;uint8_t saved[4096]; + CHECK(announce_read(&v,announcement,sizeof(announcement),&report));CHECK(root_load_messages(&value,&count,&v,wire,sizeof(wire),&report)); + CHECK(!report.malformed&&!report.refused&&report.widened==%d&&report.clamped==%d&&report.kind_mismatch==%d&&report.unknown==%d); + n=root_save(&value,saved,sizeof(saved));CHECK(n==sizeof(expected));CHECK(!memcmp(saved,expected,sizeof(expected)));return 0; +}`, octets(announcement), octets(wire), octets(expected), report.Widened, report.Clamped, report.KindMismatch, report.Unknown) + runCTableWireProbe(t, receiver, source) +} + +func TestCTableMessageZeroWidthListCapacity(t *testing.T) { + u := unitFromSource(t, `package probe + table Root { items []int32 | min=0,max=0 } + `) + slot := ir.TableVocabularySlots(u)[ir.TableFieldEntry(u.Tables["Root"].Fields[0]).Key()] + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" + int main(void){ + TableMessageEntry entries[kTableMessageEntriesHere];TableVocabulary v=table_vocabulary(entries,kTableMessageEntriesHere);TableReport report={0};TableRefuseReason reason=99; + uint8_t wire[64];TableBitWriter w=table_bit_writer(wire,sizeof(wire)); + if(!announce_read(&v,kTableAnnounce,kTableAnnounceBytes,&report))return 1; + table_bit_put(&w,2,8);table_bit_put(&w,0,8);table_bit_put(&w,%d,kTableMessageRefBitsHere);table_bit_put(&w,UINT32_MAX,32);table_bit_put(&w,0,kTableMessageRefBitsHere);table_bit_align_write(&w); + if(root_load_measure_messages_ex(&v,wire,w.bits/8,NULL,&reason)!=-1||reason!=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH)return 2; + if(report.malformed||report.refused)return 3; + return 0; + }`, slot)) +} + +func TestCTableMessagePartialRoot(t *testing.T) { + u := unitFromSource(t, `package probe +table Node { x uint32 } +table Root { node *Node +value uint64 } +`) + slot := ir.TableVocabularySlots(u)[ir.TableFieldEntry(u.Tables["Root"].Fields[1]).Key()] + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" + int main(void){ + TableMessageEntry entries[kTableMessageEntriesHere];TableVocabulary v=table_vocabulary(entries,kTableMessageEntriesHere);TableReport report={0}; + uint8_t wire[64],*region;TableBitWriter w=table_bit_writer(wire,sizeof(wire));int64_t need,count=1;const Root * root=NULL; + if(!announce_read(&v,kTableAnnounce,kTableAnnounceBytes,&report))return 1; + table_bit_put(&w,2,8);table_bit_put(&w,0,8);table_bit_put(&w,%d,kTableMessageRefBitsHere);table_bit_put(&w,99,32);table_bit_align_write(&w); + need=root_load_measure_messages(&v,wire,w.bits/8);if(need<=0)return 2;region=(uint8_t *)malloc((size_t)need);if(!region)return 3; + if(root_load_messages(&root,&count,region,need,&v,wire,w.bits/8,&report)||count!=0||root==NULL||!report.malformed||report.refused||root->value!=0){free(region);return 4;} + free(region);return 0; + }`, slot)) +} + +func TestCTableAnnouncementFailureClearsEntries(t *testing.T) { + u := unitFromSource(t, "package probe\ntable Root { a int32\nb string(8)\nc bool }\n") + runCTableWireProbe(t, u, `#include "ProbeTable.h" + int main(void){uint8_t wire[2048];TableMessageEntry entries[1];TableVocabulary v;TableReport report={0};int64_t n=announce(wire,sizeof(wire));size_t i; + if(n<=0){return 1;}memset(entries,0xa5,sizeof(entries));v=table_vocabulary(entries,1); + if(announce_read(&v,wire,n,&report)||!report.refused||report.reason!=SCHEMA_TABLE_VOCABULARY_TOO_LARGE)return 2; + for(i=0;i +static const uint8_t wire[]={%s}; +#define CHECK(x) do {if(!(x)){fprintf(stderr,"line %%d: %%s\n",__LINE__,#x);return 1;}}while(0) +int main(void){TableReport report={0};TableRetain retain={0};TableRetainId ids[32];uint8_t store[4096];const Feed * root;int64_t need=feed_load_measure(wire,sizeof(wire));uint8_t *region; +CHECK(need==136);region=(uint8_t*)malloc((size_t)need);CHECK(region); +root=feed_load(region,need,wire,sizeof(wire),&report);CHECK(root&&report.malformed&&report.unknown==1&&report.kind_mismatch==1&&!report.refused);CHECK(root->frame.type==0); +report=(TableReport){0};retain.bytes=store;retain.capacity=sizeof(store);retain.ids=ids;retain.id_capacity=32; +root=feed_load_retain(region,need,wire,sizeof(wire),&retain,&report);CHECK(root&&report.malformed&&report.unknown==1&&report.kind_mismatch==1&&!report.refused);CHECK(root->frame.type==0);free(region);return 0;} +`, cppWire(wire))) +} diff --git a/compiler/tablescretain_test.go b/compiler/tablescretain_test.go new file mode 100644 index 000000000..519715a36 --- /dev/null +++ b/compiler/tablescretain_test.go @@ -0,0 +1,247 @@ +package compiler + +import ( + "fmt" + "github.com/mas-bandwidth/schema/v2/internal/tabletext" + "github.com/mas-bandwidth/schema/v2/internal/tablewire" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestCTableRetainFile(t *testing.T) { + const old = `package probe +enum Key { A +B } +table Leaf { x uint32 } +table Many { items [..2]Leaf } +union Choice { leaf Leaf +many Many } +table Node { leaf Leaf +next *Node } +table Root { node *Node +leaf Leaf +list []Leaf +array [..3]Leaf +slots [Key]Leaf +choice Choice +entries map[int32]Leaf } +` + future := strings.Replace(old, "x uint32", "x uint32\nfuture uint64", 1) + future = strings.Replace(future, "entries map[int32]Leaf", "entries map[int32]Leaf\nextra string(32)", 1) + newer := unitFromSource(t, future) + m := tabletext.NewModel(newer) + value := m.New(m.Lookup("Root")) + var report tabletext.Report + fixture := `{"node":{"leaf":{"future":31}},"leaf":{"future":32},"list":[{"future":33},{"future":34}],"array":[{"future":35},{"future":36}],"slots":{"A":{"future":37},"B":{"future":38}},"choice":{"many":{"items":[{"future":39},{"future":40}]}},"entries":{"2":{"future":41},"7":{"future":42}},"extra":"kept"}` + if !m.Read(value, []byte(fixture), &report) || !report.Silent() { + t.Fatalf("fixture: %+v", report) + } + wire, err := tablewire.Encode(m, value) + if err != nil { + t.Fatal(err) + } + message, err := tablewire.EncodeMessages(m, []*tabletext.Instance{value, value}) + if err != nil { + t.Fatal(err) + } + var announcement, batch strings.Builder + for _, b := range tablewire.Announce(newer) { + fmt.Fprintf(&announcement, "0x%02x,", b) + } + for _, b := range message { + fmt.Fprintf(&batch, "0x%02x,", b) + } + var octets strings.Builder + for _, b := range wire { + fmt.Fprintf(&octets, "0x%02x,", b) + } + source := fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do { if(!(x)){fprintf(stderr,"line %%d: %%s\n",__LINE__,#x);return 1;} } while(0) +static const uint8_t wire[]={%s},announcement[]={%s},batch[]={%s}; +int main(void){ + uint8_t storage[8192],out[8192];TableRetainId ids[128];TableRetain keep={0};TableReport report={0};const Root * root;int64_t bytes,need,cap;uint8_t * region; + keep.bytes=storage;keep.capacity=sizeof(storage);keep.ids=ids;keep.id_capacity=128; + need=root_load_measure(wire,sizeof(wire));CHECK(need>0);region=(uint8_t *)malloc((size_t)need);CHECK(region); + root=root_load_retain(region,need,wire,sizeof(wire),&keep,&report);CHECK(root);CHECK(!report.malformed&&!report.refused&&report.unknown==13&&report.retained==13&&!report.retain_lost);CHECK(keep.count==13&&keep.id_used==0); + bytes=root_measure_retain(root,&keep);CHECK(bytes==(int64_t)sizeof(wire));CHECK(root_save_retain(root,&keep,out,bytes,&report)==bytes);CHECK(!report.retain_lost);CHECK(!memcmp(wire,out,(size_t)bytes)); + CHECK(root_save_retain(root,&keep,out,bytes,NULL)==-1); + for(cap=0;cap0);CHECK(root_save_retain(root,&keep,out,bytes,&report)==bytes);CHECK(report.retain_lost==13&&keep.id_used==0); + keep.id_capacity=128;keep.capacity=0;report=(TableReport){0};root=root_load_retain(region,need,wire,sizeof(wire),&keep,&report);CHECK(root&&report.retained==0&&report.retain_lost==13&&keep.count==0); + keep.capacity=sizeof(storage);report=(TableReport){0};root=root_load_retain(region,need,wire,sizeof(wire),&keep,&report);CHECK(root&&report.retained==13);CHECK(root_save(NULL,root,out,sizeof(out))>0); + free(region); + {TableMessageEntry entries[128];TableVocabulary vocabulary=table_vocabulary(entries,128);TableRetain retains[2]={0};TableRetainId names[2][128];uint8_t stores[2][8192];const Root * roots[2];int64_t count=2;int i; + report=(TableReport){0};CHECK(announce_read(&vocabulary,announcement,sizeof(announcement),&report));need=root_load_measure_messages(&vocabulary,batch,sizeof(batch));CHECK(need>0);region=(uint8_t *)malloc((size_t)need);CHECK(region); + for(i=0;i<2;i++){retains[i].bytes=stores[i];retains[i].capacity=sizeof(stores[i]);retains[i].ids=names[i];retains[i].id_capacity=128;} + CHECK(root_load_retain_messages(roots,&count,region,need,&vocabulary,batch,sizeof(batch),retains,&report));CHECK(count==2);CHECK(!report.malformed&&!report.refused&&report.retained==26&&!report.retain_lost); + for(i=0;i<2;i++){bytes=root_measure_retain(roots[i],retains+i);CHECK(bytes==sizeof(wire));CHECK(root_save_retain(roots[i],retains+i,out,bytes,&report)==bytes);CHECK(!report.retain_lost&&!memcmp(wire,out,(size_t)bytes));} + free(region);} + return 0; +} +`, octets.String(), announcement.String(), batch.String()) + runCTableWireProbe(t, unitFromSource(t, old), source) +} + +func TestCTableRetainCapacityBoundsZeroBitMessage(t *testing.T) { + old := unitFromSource(t, "package probe\ntable Root { next *Root }\n") + future := unitFromSource(t, "package probe\ntable Root { next *Root\nzeroes []int32 | min = 0, max = 0 }\n") + var a strings.Builder + for _, b := range tablewire.Announce(future) { + fmt.Fprintf(&a, "0x%02x,", b) + } + runCTableWireProbe(t, old, fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do {if(!(x)){fprintf(stderr,"line %%d: %%s\n",__LINE__,#x);return 1;}} while(0) +static const uint8_t announcement[]={%s}; +int main(void){TableMessageEntry entries[64];TableVocabulary vocabulary=table_vocabulary(entries,64);TableReport report={0};TableBitWriter w;TableRetain retain={0};TableRetainId ids[16];uint8_t wire[64],store[4097];const Root * roots[1];uint8_t * region;int64_t count=1,need;int i;uint64_t ref=0; + CHECK(announce_read(&vocabulary,announcement,sizeof(announcement),&report));for(i=0;i0);region=(uint8_t *)malloc((size_t)need);CHECK(region);memset(store,0xa5,sizeof(store));retain.bytes=store;retain.capacity=sizeof(store)-1;retain.ids=ids;retain.id_capacity=16; + CHECK(root_load_retain_messages(roots,&count,region,need,&vocabulary,wire,w.bits/8,&retain,&report));CHECK(count==1&&!report.malformed&&!report.refused&&report.unknown==1&&report.retain_lost==1&&report.retained==0);CHECK(retain.used==0&&retain.count==0&&store[sizeof(store)-1]==0xa5);free(region);return 0; +}`, a.String())) +} + +func TestCTableRetainFramedDepth(t *testing.T) { + old := unitFromSource(t, "package probe\ntable Root { next *Root }\n") + for _, depth := range []int{64, 65} { + t.Run(fmt.Sprint(depth), func(t *testing.T) { + var schema, fixture strings.Builder + schema.WriteString("package probe\ntable Root { next *Root\nextra T0 }\n") + fixture.WriteString(`{"extra":`) + for i := range depth { + if i+1 < depth { + fmt.Fprintf(&schema, "table T%d { nested T%d }\n", i, i+1) + fixture.WriteString(`{"nested":`) + } else { + fmt.Fprintf(&schema, "table T%d { x uint8 }\n", i) + fixture.WriteString(`{"x":7}`) + } + } + fixture.WriteString(strings.Repeat("}", depth)) + future := unitFromSource(t, schema.String()) + m := tabletext.NewModel(future) + value := m.New(m.Lookup("Root")) + var report tabletext.Report + if !m.Read(value, []byte(fixture.String()), &report) || !report.Silent() { + t.Fatalf("fixture: %+v", report) + } + wire, err := tablewire.Encode(m, value) + if err != nil { + t.Fatal(err) + } + batch, err := tablewire.EncodeMessages(m, []*tabletext.Instance{value}) + if err != nil { + t.Fatal(err) + } + announcement := tablewire.Announce(future) + var octets strings.Builder + for _, b := range wire { + fmt.Fprintf(&octets, "0x%02x,", b) + } + kept := 0 + if depth == 64 { + kept = 1 + } + runCTableWireProbe(t, old, fmt.Sprintf(`#include "ProbeTable.h" +static const uint8_t wire[]={%s}; +int main(void){TableRetain retain={0};TableRetainId ids[256];TableReport report={0};uint8_t store[8192],out[8192];const Root * root;int64_t need=root_load_measure(wire,sizeof(wire)),bytes;uint8_t * region;if(need<0)return 1;region=(uint8_t *)malloc((size_t)need);if(!region)return 2;retain.bytes=store;retain.capacity=sizeof(store);retain.ids=ids;retain.id_capacity=256;root=root_load_retain(region,need,wire,sizeof(wire),&retain,&report);if(!root||report.malformed||report.refused||report.unknown!=1||report.retained!=%d||report.retain_lost!=%d)return 3;bytes=root_measure_retain(root,&retain);if(bytes<0||root_save_retain(root,&retain,out,sizeof(out),&report)!=bytes)return 4;free(region);return 0;} +`, octets.String(), kept, 1-kept)) + runCTableWireProbe(t, old, fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do{if(!(x)){fprintf(stderr,"message depth line %%d: %%s\n",__LINE__,#x);return 1;}}while(0) +static const uint8_t batch[]={%s},announcement[]={%s},wire[]={%s}; +int main(void){TableMessageEntry entries[256];TableVocabulary vocabulary=table_vocabulary(entries,256);TableReport report={0};TableRetain retain={0};TableRetainId ids[256];uint8_t store[8192],out[8192];const Root * roots[1];int64_t need,count=1,bytes;uint8_t * region; +CHECK(announce_read(&vocabulary,announcement,sizeof(announcement),&report));need=root_load_measure_messages(&vocabulary,batch,sizeof(batch));CHECK(need>0);region=(uint8_t*)malloc((size_t)need);CHECK(region);retain.bytes=store;retain.capacity=sizeof(store);retain.ids=ids;retain.id_capacity=256; +CHECK(root_load_retain_messages(roots,&count,region,need,&vocabulary,batch,sizeof(batch),&retain,&report));CHECK(count==1&&!report.malformed&&!report.refused&&report.unknown==1&&report.retained==%d&&report.retain_lost==%d);bytes=root_measure_retain(roots[0],&retain);CHECK(bytes>0);CHECK(root_save_retain(roots[0],&retain,out,sizeof(out),&report)==bytes);if(report.retained){CHECK(bytes==sizeof(wire));CHECK(memcmp(wire,out,sizeof(wire))==0);}free(region);return 0;} +`, cppWire(batch), cppWire(announcement), cppWire(wire), kept, 1-kept)) + + }) + } +} + +func TestCTableRetainRefusedByName(t *testing.T) { + cc, err := exec.LookPath("cc") + if err != nil { + t.Skip("requires C compiler") + } + u := unitFromSource(t, "package probe\ntable Fixed { x uint32 }\ntable Variable { next *Variable }\n") + files, err := New().Generate(u, "c", Options{}) + if err != nil { + t.Fatal(err) + } + dir := t.TempDir() + for name, data := range files { + if err := os.WriteFile(filepath.Join(dir, name), data, 0600); err != nil { + t.Fatal(err) + } + } + for _, call := range []string{"fixed_load_retain()", "fixed_measure_retain()", "fixed_save_retain()", "fixed_load_retain_messages()", "variable_save_retain_messages()"} { + t.Run(call, func(t *testing.T) { + path := filepath.Join(dir, "refusal.c") + if err := os.WriteFile(path, []byte("#include \"ProbeTable.h\"\nint main(void){"+call+";return 0;}\n"), 0600); err != nil { + t.Fatal(err) + } + out, err := exec.Command(cc, "-std=c99", "-fsyntax-only", "-I", dir, path).CombinedOutput() + if err == nil || !strings.Contains(string(out), "retention_") { + t.Fatalf("expected named refusal: %v\n%s", err, out) + } + }) + } +} + +// Keys are probed before entry placement: clipping one would invent a key, +// and a dropped entry contributes no decoded fields or retained records. +func TestCTableRetainMessageDroppedMapKey(t *testing.T) { + const old = `package probe + table Item { number int32 } + table Root { names map[string(2)]Item } + ` + newer := strings.Replace(strings.Replace(old, "string(2)", "string(8)", 1), "number int32", "number int32\nextra int32", 1) + sender, receiver := unitFromSource(t, newer), unitFromSource(t, old) + sm, rm := tabletext.NewModel(sender), tabletext.NewModel(receiver) + octets := func(b []byte) string { + var s strings.Builder + for _, v := range b { + fmt.Fprintf(&s, "0x%02x,", v) + } + return s.String() + } + for _, replaced := range []bool{false, true} { + t.Run(fmt.Sprint(replaced), func(t *testing.T) { + source, next := sm.New(sm.Lookup("Root")), sm.New(sm.Lookup("Root")) + var report tabletext.Report + if !sm.Read(source, []byte(`{"names":{"long":{"number":1,"extra":7}}}`), &report) || !report.Silent() { + t.Fatal(report) + } + expected := rm.New(rm.Lookup("Root")) + if replaced { + text := []byte(`{"names":{"ok":{"number":2}}}`) + if !sm.Read(next, text, &report) || !rm.Read(expected, text, &report) || !report.Silent() { + t.Fatal(report) + } + source.Fields = append(source.Fields, next.Fields[0]) + } + batch, err := tablewire.EncodeMessages(sm, []*tabletext.Instance{source}) + if err != nil { + t.Fatal(err) + } + want, err := tablewire.Encode(rm, expected) + if err != nil { + t.Fatal(err) + } + runCTableWireProbe(t, receiver, fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do{if(!(x)){fprintf(stderr,"line %%d: %%s\n",__LINE__,#x);return 1;}}while(0) +static const uint8_t announcement[]={%s},batch[]={%s},want[]={%s}; +int main(void){TableMessageEntry entries[128];TableVocabulary vocabulary=table_vocabulary(entries,128);TableReport report={0};TableRetain keep={0};TableRetainId ids[128];uint8_t store[8192],out[8192];uint8_t *region;const Root *roots[1];int64_t count=1,need,bytes; + CHECK(announce_read(&vocabulary,announcement,sizeof(announcement),&report));need=root_load_measure_messages(&vocabulary,batch,sizeof(batch));CHECK(need>0);region=(uint8_t *)malloc((size_t)need);CHECK(region);keep.bytes=store;keep.capacity=sizeof(store);keep.ids=ids;keep.id_capacity=128; + CHECK(root_load_retain_messages(roots,&count,region,need,&vocabulary,batch,sizeof(batch),&keep,&report));CHECK(count==1&&!report.malformed&&!report.refused&&report.clamped==1&&report.unknown==0&&report.retained==0&&report.retain_lost==0); + bytes=root_save_retain(roots[0],&keep,out,sizeof(out),&report);CHECK(bytes==sizeof(want)&&!memcmp(out,want,sizeof(want))&&report.retain_lost==0);free(region);return 0; +}`, octets(tablewire.Announce(sender)), octets(batch), octets(want))) + }) + } +} diff --git a/compiler/tablescview_test.go b/compiler/tablescview_test.go new file mode 100644 index 000000000..40503d2ea --- /dev/null +++ b/compiler/tablescview_test.go @@ -0,0 +1,87 @@ +package compiler + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/mas-bandwidth/schema/v2/internal/viewlisting" +) + +func TestCTableViewOutsideClosure(t *testing.T) { + for _, tables := range []bool{false, true} { + t.Run(map[bool]string{false: "packet", true: "tables"}[tables], func(t *testing.T) { + source := viewSrc + if !tables { + source = strings.Replace(source, "table Root", "type Root", 1) + } + u := unitFromSource(t, source) + runCTableWireProbe(t, u, `#include "ProbeView.h" +#include +#define CHECK(x) do { if(!(x)) { fprintf(stderr,"line %d: %s\n",__LINE__,#x); return 1; } } while(0) +int main(void) { + const UnitViewInfo * view=unit_view(); const TableTypeInfo * type; Orphan value; int i; + CHECK(view==unit_view() && !strcmp(view->package,"probe")); + CHECK(view->num_enums==1 && view->num_flags==1 && view->num_unions==0); + CHECK(view->enums[0].num_variants==3 && !strcmp(view->enums[0].variants[1].name,"Alpha")); + CHECK(view->enums[0].variants[1].id==0 && view->flags[0].variants[1].value==1); + CHECK(view->num_types>=2 && !strcmp(view->types[0].name,"Orphan")); + type=view->types[0].type; CHECK(type==orphan_table_type() && type->num_fields==4); + CHECK(!strcmp(type->doc,"A type no table reaches.") && type->num_tags==1 && !strcmp(type->tags[0],"inspected")); + for(i=0;inum_fields;i++) { CHECK(type->fields[i].id==0 && type->fields[i].json==NULL); } + CHECK(type->fields[0].variants[1].id==0 && type->fields[3].keys[2].id==0); + memset(&value,0xa5,sizeof(value));type->reset(&value);CHECK(value.grade==0 && value.label_length==0 && value.slots[0]==0); + return 0; +} + +`) + }) + } +} + +func TestCTableViewListing(t *testing.T) { + for _, path := range []string{"../tables/examples", "../tables/pointers", "../tables/maps", "../tables/lists", "../tables/arms", "../examples", "../examples-wide"} { + t.Run(filepath.Base(path), func(t *testing.T) { + c := New() + paths, err := filepath.Glob(filepath.Join(path, "*.schema")) + if err != nil { + t.Fatal(err) + } + u, err := c.Load(paths) + if err != nil { + t.Fatal(err) + } + files, err := c.Generate(u, "c", Options{}) + if err != nil { + t.Fatal(err) + } + dir := t.TempDir() + args := []string{"-std=c99", "-Wall", "-Wextra", "-Werror", "-O1", "-I", dir} + base := strings.ToUpper(u.Package[:1]) + u.Package[1:] + "View" + args = append(args, "-DVIEW_HEADER=\""+base+".h\"", "../test/c-tables/view_main.c") + for name, data := range files { + p := filepath.Join(dir, name) + if err := os.WriteFile(p, data, 0600); err != nil { + t.Fatal(err) + } + if strings.HasSuffix(name, ".c") { + args = append(args, p) + } + } + binary := filepath.Join(dir, "view") + args = append(args, "-lm", "-o", binary) + if out, err := exec.Command("cc", args...).CombinedOutput(); err != nil { + t.Fatalf("compile: %v\n%s", err, out) + } + out, err := exec.Command(binary).CombinedOutput() + if err != nil { + t.Fatalf("run: %v\n%s", err, out) + } + if string(out) != viewlisting.Listing(u) { + t.Fatalf("registry listing differs\nexpected:\n%s\ngot:\n%s", viewlisting.Listing(u), out) + } + }) + } +} diff --git a/compiler/tablescwire_test.go b/compiler/tablescwire_test.go new file mode 100644 index 000000000..8b87b158d --- /dev/null +++ b/compiler/tablescwire_test.go @@ -0,0 +1,768 @@ +package compiler + +import ( + "context" + "encoding/binary" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/mas-bandwidth/schema/v2/internal/tabletext" + "github.com/mas-bandwidth/schema/v2/internal/tablewire" + "github.com/mas-bandwidth/schema/v2/ir" +) + +// Exercise generated code across the first-use reference-width boundary. The +// expectation is produced by the independent compiler engine, not the emitter. +// Nested frames must measure with the vocabulary already accumulated by their +// parent, and repeated saves must not retain a previous call's vocabulary. +func TestCTableWireReferenceBoundary(t *testing.T) { + var schema, fill strings.Builder + schema.WriteString(`package probe + +enum Choice { +Current +Second +} +type Leaf { choice Choice } +table Root { +`) + for i := range 127 { + fmt.Fprintf(&schema, "field%d uint8\n", i) + fmt.Fprintf(&fill, " value.field%d = 1;\n", i) + } + schema.WriteString("leaf Leaf | was = \"oldLeaf\"\nleaves [..2]Leaf\nslots [Choice]Leaf\n}\n") + fill.WriteString(" value.leaf.choice = CHOICE_CURRENT;\n value.leaves_count = 2;\n value.leaves[0].choice = CHOICE_SECOND;\n value.leaves[1].choice = CHOICE_CURRENT;\n value.slots[0].choice = CHOICE_SECOND;\n") + u := unitFromSource(t, schema.String()) + model := tabletext.NewModel(u) + value := model.New(u.Tables["Root"]) + for i := range 127 { + value.Fields[i].Cell.U = 1 + } + value.Fields[127].Cell.Tab.Fields[0].Cell.U = 1 + value.Fields[128].Count = 2 + value.Fields[128].Elems[0].Tab.Fields[0].Cell.U = 2 + value.Fields[128].Elems[1].Tab.Fields[0].Cell.U = 1 + value.Fields[129].Elems[0].Tab.Fields[0].Cell.U = 2 + expected, err := tablewire.Encode(model, value) + if err != nil { + t.Fatal(err) + } + if count := binary.LittleEndian.Uint64(expected[len(expected)-8:]); count <= 128 { + t.Fatalf("test did not cross the identity boundary: %d", count) + } + var octets strings.Builder + for _, b := range expected { + fmt.Fprintf(&octets, "0x%02x,", b) + } + source := fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do { if (!(x)) { fprintf(stderr, "line %%d: %%s\n", __LINE__, #x); return 1; } } while (0) +static const uint8_t expected[] = { %s }; +static void set_keyed(Leaf * slots,int32_t key) { SCHEMA_TABLE_KEYED_AT(slots,key,CHOICE_MAX).choice=CHOICE_SECOND; } +int main(void) +{ + Root value, decoded; + TableReport report; + uint8_t saved[sizeof(expected) + 1], damaged[sizeof(expected)]; + int64_t n; + root_reset(&value); +%s + set_keyed(value.slots,CHOICE_CURRENT); + n = root_measure(&value); + CHECK(n == (int64_t)sizeof(expected)); + CHECK(root_save(&value, saved, n) == n); + CHECK(memcmp(saved, expected, sizeof(expected)) == 0); + CHECK(root_save(&value, saved, n) == n); + CHECK(memcmp(saved, expected, sizeof(expected)) == 0); + saved[n-1] = 0x7d; + CHECK(root_save(&value, saved, n-1) == -1); + CHECK(saved[n-1] == 0x7d); + CHECK(root_save(&value, saved, -1) == -1); + CHECK(root_save(&value, NULL, n) == -1); + memset(&report, 0, sizeof(report)); + CHECK(root_load(&decoded, expected, n, &report)); + CHECK(!report.malformed && !report.refused && !report.unknown && !report.kind_mismatch && !report.widened && !report.clamped); + CHECK(root_save(&decoded, saved, n) == n); + CHECK(memcmp(saved, expected, sizeof(expected)) == 0); + CHECK(root_load(&decoded, expected, n, NULL)); + memcpy(damaged, expected, sizeof(expected)); + damaged[0] = 2; + memset(&report, 0, sizeof(report)); + CHECK(!root_load(&decoded, damaged, n, &report)); + CHECK(report.refused && report.reason == SCHEMA_TABLE_MESSAGE_FORM_AS_FILE && !report.malformed); + CHECK(root_measure(&decoded) == 10); + damaged[0] = 99; + memset(&report, 0, sizeof(report)); + CHECK(!root_load(&decoded, damaged, n, &report)); + CHECK(report.refused && report.reason == SCHEMA_TABLE_NEWER_FORM && !report.malformed); + memset(&report, 0, sizeof(report)); + CHECK(!root_load(&decoded, NULL, n, &report) && report.malformed && !report.refused); + return 0; +} +`, octets.String(), fill.String()) + runCTableWireProbe(t, u, source) +} + +func runCTableWireProbe(t *testing.T, u *ir.Unit, source string) { + t.Helper() + cc, err := exec.LookPath("cc") + if err != nil { + t.Skip("generated C execution requires cc") + } + files, err := New().Generate(u, "c", Options{}) + if err != nil { + t.Fatal(err) + } + dir := t.TempDir() + for name, data := range files { + if err := os.WriteFile(filepath.Join(dir, name), data, 0600); err != nil { + t.Fatal(err) + } + } + if err := os.WriteFile(filepath.Join(dir, "main.c"), []byte(source), 0600); err != nil { + t.Fatal(err) + } + args := []string{"-std=c99", "-Wall", "-Wextra", "-Werror", "-Wshadow", "-O2", "-I", dir, filepath.Join(dir, "main.c")} + for name := range files { + if strings.HasSuffix(name, ".c") { + args = append(args, filepath.Join(dir, name)) + } + } + args = append(args, "-lm", "-o", filepath.Join(dir, "probe")) + cmd := exec.Command(cc, args...) + if output, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("compile: %v\n%s", err, output) + } + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + if output, err := exec.CommandContext(ctx, filepath.Join(dir, "probe")).CombinedOutput(); err != nil { + t.Fatalf("execute: %v\n%s", err, output) + } +} + +// Clamps must happen after promoting the source-width integer. NaN widening +// must preserve its sign and payload without quieting a signaling NaN. +func TestCTableWireWidening(t *testing.T) { + u := unitFromSource(t, `package probe + table Root { + low int32 = 1000 | min = 1000, max = 2000 + high int32 = -1000 | min = -2000, max = -1000 + positive uint64 = 1000 | min = 1000, max = 2000 + real float64 + samples [..2]uint32 | min = 0, max = 2000 + } + `) + wire := []byte{1, 1, 2, 255, 2, 2, 0, 3, 6, 255, 4, 10} + wire = binary.LittleEndian.AppendUint32(wire, 0xffa5a5a5) + wire = append(wire, 5, 14, 6, 7, 2, 0, 0, 255, 255, 0) + for _, f := range u.Tables["Root"].Fields { + wire = binary.LittleEndian.AppendUint64(wire, ir.TableFieldWireId(f)) + } + wire = binary.LittleEndian.AppendUint64(wire, 5) + model := tabletext.NewModel(u) + value := model.New(u.Tables["Root"]) + var report tabletext.Report + if ok, err := tablewire.Decode(model, value, wire, &report); err != nil || !ok { + t.Fatalf("oracle decode: %v %v", ok, err) + } + if report.Widened != 5 || report.Clamped != 4 || report.Malformed { + t.Fatalf("test did not exercise five widenings and four clamps: %+v", report) + } + if value.Fields[2].Cell.U != 1000 { + t.Fatalf("oracle lost the widened clamp: %d", value.Fields[2].Cell.U) + } + saved, err := tablewire.Encode(model, value) + if err != nil { + t.Fatal(err) + } + octets := func(data []byte) string { + var s strings.Builder + for _, b := range data { + fmt.Fprintf(&s, "0x%02x,", b) + } + return s.String() + } + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" + #include + static const uint8_t wire[] = { %s }, expected[] = { %s }; + int main(void) { + Root value; TableReport report; uint8_t saved[sizeof(expected)]; + memset(&report, 0, sizeof(report)); + if (!root_load(&value, wire, sizeof(wire), &report)) return 1; + if (report.widened != 5 || report.clamped != 4 || report.malformed || report.refused) return 2; + if (root_measure(&value) != (int64_t)sizeof(expected)) { fprintf(stderr, "measure %%lld expected %%zu: low %%d high %%d positive %%llu samples %%d [%%u, %%u]\n", (long long)root_measure(&value), sizeof(expected), value.low, value.high, (unsigned long long)value.positive, value.samples_count, value.samples[0], value.samples[1]); return 3; } + if (root_save(&value, saved, sizeof(saved)) != (int64_t)sizeof(expected)) return 4; + return memcmp(saved, expected, sizeof(expected)) != 0; + } + `, octets(wire), octets(saved))) +} + +// A damaged later occurrence resets text to its declared default; an explicit +// empty value remains an edit and survives a save/load cycle. +func TestCTableWireDefaultRecovery(t *testing.T) { + u := unitFromSource(t, `package probe +flags Caps { Jump, Crouch } +table Root { + name string(8) = "new" + data bytes(4) = "ab" + caps Caps = { Jump } +} +`) + wire := []byte{1, 1, 12, 2, 'o', 'k', 1, 12, 1, 0xff, 0} + wire = binary.LittleEndian.AppendUint64(wire, ir.TableFieldWireId(u.Tables["Root"].Fields[0])) + wire = binary.LittleEndian.AppendUint64(wire, 1) + var octets strings.Builder + for _, b := range wire { + fmt.Fprintf(&octets, "0x%02x,", b) + } + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do { if (!(x)) { fprintf(stderr,"line %%d: %%s\n",__LINE__,#x); return 1; } } while(0) +static const uint8_t damaged[] = {%s}; +int main(void) { + Root value, copy; TableReport report; uint8_t out[256]; int64_t n; + root_reset(&value); + CHECK(value.name_length == 3 && !memcmp(value.name,"new",4)); + CHECK(value.data_length == 2 && !memcmp(value.data,"ab",2)); + CHECK(value.caps == CAPS_JUMP && root_measure(&value) == 10); + memset(&report,0,sizeof(report)); + CHECK(root_load(&value,damaged,sizeof(damaged),&report)); + CHECK(report.malformed && value.name_length == 3 && !memcmp(value.name,"new",4)); + value.name_length = 0; value.name[0] = 0; value.data_length = 0; value.caps = 0; + n=root_save(&value,out,sizeof(out)); CHECK(n > 10); + memset(&report,0,sizeof(report)); + CHECK(root_load(©,out,n,&report)); + CHECK(!report.malformed && copy.name_length == 0 && copy.data_length == 0 && copy.caps == 0); + return 0; +} +`, octets.String())) +} + +// Integer JSON conversion keeps a magnitude unsigned until its domain is +// established. Large unsigned tokens and negative exponents must never pass +// through an out-of-range float-to-integer conversion or wrap to zero. +func TestCTableWireJsonIntegerDomain(t *testing.T) { + u := unitFromSource(t, `package probe +table Root { + small uint8 + bounded uint64 | min = 0, max = 18446744073709551615 + debt int64 +} +`) + runCTableWireProbe(t, u, `#include "ProbeTable.h" +#include +#define CHECK(x) do { if (!(x)) { fprintf(stderr,"line %d: %s\n",__LINE__,#x); return 1; } } while(0) +int main(void) { + Root value; TableReport report; + const char * first="{\"small\":18446744073709551615,\"bounded\":18446744073709551615,\"debt\":-9223372036854775808}"; + const char * second="{\"small\":-1e30,\"bounded\":-1e30}"; + memset(&report,0,sizeof(report)); + CHECK(root_from_json(&value,first,(int64_t)strlen(first),&report)); + CHECK(value.small == 255 && value.bounded == UINT64_MAX && value.debt == INT64_MIN); + CHECK(report.clamped == 1 && !report.kind_mismatch && !report.malformed); + memset(&report,0,sizeof(report)); + CHECK(root_from_json(&value,second,(int64_t)strlen(second),&report)); + CHECK(value.small == 0 && value.bounded == 0 && report.clamped == 4); + CHECK(!report.kind_mismatch && !report.malformed); + return 0; +} +`) +} + +// A deep by-value closure must not cause exponential frame remeasurement. +// Only the leaf rides, so checking each ancestor for elision should stop at +// that first field and measuring its frame must walk the payload once. +func TestCTableWireDeepFrames(t *testing.T) { + var schema, member strings.Builder + schema.WriteString("package probe\ntable Layer0 { value uint8 }\n") + for i := 1; i <= 24; i++ { + fmt.Fprintf(&schema, "table Layer%d { child Layer%d }\n", i, i-1) + member.WriteString(".child") + } + schema.WriteString("table Root { child Layer24 }\n") + u := unitFromSource(t, schema.String()) + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" +int main(void) { + Root value, copy; uint8_t out[1024]; int64_t n; TableReport report; + root_reset(&value); value.child%s.value=1; + n=root_measure(&value); + if(n<=10 || n>1024 || root_save(&value,out,n)!=n) return 1; + memset(&report,0,sizeof(report)); + if(!root_load(©,out,n,&report) || report.malformed || copy.child%s.value!=1) return 2; + return 0; +} +`, member.String(), member.String())) +} + +func TestCTableWireGraphIdentity(t *testing.T) { + u := unitFromSource(t, `package probe +table Node { + value uint32 + next *Node +} + +table Root { + first *Node + slots [..3]*Node + peers [2]*Node +} +`) + model := tabletext.NewModel(u) + root := model.New(u.Tables["Root"]) + first, second := model.New(u.Tables["Node"]), model.New(u.Tables["Node"]) + first.Fields[0].Cell.U = 7 + second.Fields[0].Cell.U = 9 + first.Fields[1].Cell.Node = second + root.Fields[0].Cell.Node = first + root.Fields[1].Count = 3 + root.Fields[1].Elems[0].Node = second + root.Fields[1].Elems[2].Node = first + root.Fields[2].Elems[0].Node = second + expected, err := tablewire.Encode(model, root) + if err != nil { + t.Fatal(err) + } + var octets strings.Builder + for _, b := range expected { + fmt.Fprintf(&octets, "0x%02x,", b) + } + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do { if(!(x)) { fprintf(stderr,"line %%d: %%s\n",__LINE__,#x); return 1; } } while(0) +static const uint8_t expected[]={%s}; +int main(void) { + RootBuilder builder,copy; Root * root; Node * first; Node * second; + const Root * locked; const Root * loaded; TableSink sink; TableCtx ctx; + char text[4096]; int64_t text_bytes; + uint8_t saved[sizeof(expected)],*region; int64_t extent,attribution; TableRefuseReason reason=99; TableReport report; + CHECK(root_builder_init(&builder)); root=root_builder_root(&builder); + ctx.arena=&builder.arena; sink.region=NULL; sink.worker=&builder.main; + first=node_emplace(&sink,&root->first); CHECK(first!=NULL);first->value=7; + second=node_emplace(&sink,&first->next); CHECK(second!=NULL);second->value=9; + root->slots_count=3;root->slots[0]=first->next;root->slots[2]=root->first;root->peers[0]=first->next; + CHECK(root_measure(&ctx,root)==(int64_t)sizeof(expected)); + CHECK(root_save(&ctx,root,saved,sizeof(saved))==(int64_t)sizeof(expected)); + CHECK(!memcmp(saved,expected,sizeof(saved))); + second->next=root->first; + CHECK(root_measure(&ctx,root)==-1 && !root_builder_lock(&builder)); + second->next.value=0; + CHECK(root_builder_lock(&builder));locked=(const Root *)(const void *)builder.region; + CHECK(node_at(NULL,&locked->slots[2])==node_at(NULL,&locked->first)); + CHECK(node_at(NULL,&locked->slots[0])==node_at(NULL,&locked->peers[0])); + CHECK(root_save(NULL,locked,saved,sizeof(saved))==(int64_t)sizeof(expected)); + CHECK(!memcmp(saved,expected,sizeof(saved))); + extent=root_load_measure_ex(expected,sizeof(expected),&attribution,&reason); + CHECK(extent>0 && attribution==3*(int64_t)sizeof(TableNodeDirEntry) && reason==99); + region=(uint8_t *)malloc((size_t)extent);CHECK(region!=NULL);memset(&report,0,sizeof(report)); + loaded=root_load(region,extent,expected,sizeof(expected),&report); + CHECK(loaded!=NULL && !report.malformed && !report.unknown && !report.kind_mismatch); + CHECK(node_at(NULL,&loaded->slots[2])==node_at(NULL,&loaded->first)); + CHECK(loaded->slots[1].value==0 && loaded->slots_count==3); + CHECK(root_builder_init(©));CHECK(root_load_builder(©,expected,sizeof(expected),&report)); + CHECK(root_builder_lock(©)); + CHECK(root_save(NULL,(const Root *)(const void *)copy.region,saved,sizeof(saved))==(int64_t)sizeof(expected)); + CHECK(!memcmp(saved,expected,sizeof(saved))); + root_builder_shutdown(©); + text_bytes=root_to_json_measure(loaded);CHECK(text_bytes>0 && text_bytes<4096); + CHECK(root_to_json(loaded,text,sizeof(text))==text_bytes); + CHECK(root_builder_init(©));memset(&report,0,sizeof(report)); + CHECK(root_from_json(©,text,text_bytes,&report));CHECK(!report.malformed && !report.kind_mismatch && !report.unknown); + CHECK(root_builder_lock(©)); + CHECK(root_save(NULL,(const Root *)(const void *)copy.region,saved,sizeof(saved))==(int64_t)sizeof(expected)); + CHECK(!memcmp(saved,expected,sizeof(saved))); + root_builder_shutdown(©);root_builder_shutdown(&builder);free(region);return 0; +} +`, octets.String())) +} + +func TestCTableWireBlobIdentity(t *testing.T) { + u := unitFromSource(t, `package probe +table Root { + data *bytes + alias *bytes + text *string + empty *bytes +}`) + model := tabletext.NewModel(u) + value := model.New(u.Tables["Root"]) + blob := &tabletext.Blob{Data: []byte{0, 255, 42}} + value.Fields[0].Cell.Blob = blob + value.Fields[1].Cell.Blob = blob + value.Fields[2].Cell.Blob = &tabletext.Blob{Data: []byte("hello")} + value.Fields[3].Cell.Blob = &tabletext.Blob{} + expected, err := tablewire.Encode(model, value) + if err != nil { + t.Fatal(err) + } + var octets strings.Builder + for _, b := range expected { + fmt.Fprintf(&octets, "0x%02x,", b) + } + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do { if(!(x)) { fprintf(stderr,"line %%d: %%s\n",__LINE__,#x); return 1; } } while(0) +static const uint8_t expected[]={%s}; +int main(void) { + RootBuilder builder,copy; Root * root; const Root * locked; TableCtx ctx; + uint8_t saved[sizeof(expected)],*data,*region; int64_t extent; TableReport report={0}; + CHECK(root_builder_init(&builder));root=root_builder_root(&builder);ctx.arena=&builder.arena; + data=table_bytes_emplace(&builder.main,&root->data,3);CHECK(data!=NULL);data[0]=0;data[1]=255;data[2]=42; + root->alias=root->data; + CHECK(table_string_emplace(&builder.main,&root->text,"hello",5)!=NULL); + CHECK(table_bytes_emplace(&builder.main,&root->empty,0)!=NULL); + CHECK(root_measure(&ctx,root)==(int64_t)sizeof(expected)); + CHECK(root_save(&ctx,root,saved,sizeof(saved))==(int64_t)sizeof(expected));CHECK(!memcmp(saved,expected,sizeof(saved))); + CHECK(root_builder_lock(&builder));locked=(const Root *)(const void *)builder.region; + CHECK(table_bytes_at(NULL,&locked->data).data==table_bytes_at(NULL,&locked->alias).data); + CHECK(table_bytes_at(NULL,&locked->empty).data!=NULL && table_bytes_at(NULL,&locked->empty).length==0); + CHECK(!strcmp(table_string_at(NULL,&locked->text).data,"hello")); + CHECK(root_to_json_measure(locked)==-1); + extent=root_load_measure(expected,sizeof(expected));CHECK(extent>0);region=(uint8_t *)malloc((size_t)extent);CHECK(region!=NULL); + locked=root_load(region,extent,expected,sizeof(expected),&report);CHECK(locked!=NULL && !report.malformed && !report.unknown); + CHECK(table_bytes_at(NULL,&locked->data).data==table_bytes_at(NULL,&locked->alias).data); + CHECK(!strcmp(table_string_at(NULL,&locked->text).data,"hello")); + CHECK(root_builder_init(©));CHECK(root_load_builder(©,expected,sizeof(expected),&report)); + CHECK(root_builder_lock(©));CHECK(root_save(NULL,(const Root *)(const void *)copy.region,saved,sizeof(saved))==(int64_t)sizeof(expected)); + CHECK(!memcmp(saved,expected,sizeof(saved)));root_builder_shutdown(©);root_builder_shutdown(&builder);free(region); + CHECK(root_builder_init(&builder));root=root_builder_root(&builder); + data=table_bytes_emplace(&builder.main,&root->data,(int64_t)kTableSegmentSize+17);CHECK(data!=NULL); + data[0]=31;data[kTableSegmentSize+16]=73; + CHECK(root_builder_lock(&builder));locked=(const Root *)(const void *)builder.region; + CHECK(table_bytes_at(NULL,&locked->data).length==(int64_t)kTableSegmentSize+17); + CHECK(table_bytes_at(NULL,&locked->data).data[0]==31 && table_bytes_at(NULL,&locked->data).data[kTableSegmentSize+16]==73); + root_builder_shutdown(&builder);return 0; +} +`, octets.String())) +} + +// Fail each allocation in a complete authoring round trip. Every path must +// return to its caller and release through the same pair that allocated it. +func TestCTableWireAllocatorFailures(t *testing.T) { + u := unitFromSource(t, `package probe +table Node { value uint32 } +table Payload { entries map[int32]*Node } +table Root { + first *Node + second *Node + children []Payload + rows map[string(8)]Node +}`) + runCTableWireProbe(t, u, `#include "ProbeTable.h" +#include +typedef struct Counts { int calls,fail,live; } Counts; +static void * allocate(void * context,int64_t bytes) { + Counts * c=(Counts *)context; void * p; + if(c->calls++==c->fail) return NULL; + p=calloc(1,(size_t)bytes);if(p!=NULL)c->live++;return p; +} +static void release(void * context,void * p) { + Counts * c=(Counts *)context;if(p!=NULL)c->live--;free(p); +} +static int tail_intact(const uint8_t * bytes,int64_t used,int64_t capacity) { + int64_t i;for(i=used;i2048)goto done; + if(root_save(&ctx,root_builder_root(&b),wire,n)!=n)goto done; + if(!root_builder_lock(&b))goto done; + if(root_measure_with_allocator(NULL,(const Root *)(const void *)b.region,a)!=n)goto done; + memset(wire,0xa5,sizeof(wire));result=root_save_with_allocator(NULL,(const Root *)(const void *)b.region,wire,n,a); + if(!tail_intact(wire,n,sizeof(wire))){abort();}if(result!=n)goto done; + m=root_to_json_measure_with_allocator((const Root *)(const void *)b.region,a);if(m<=0 || m>1024)goto done; + if(root_to_json_with_allocator((const Root *)(const void *)b.region,text,m,a)!=m)goto done; + m=root_cook_measure_with_allocator(NULL,(const Root *)(const void *)b.region,a);if(m<=0 || m>4096)goto done; + {int order;for(order=0;order<2;order++){ + memset(cook,0xa5,sizeof(cook));result=root_cook_with_allocator(NULL,(const Root *)(const void *)b.region,cook,(uint64_t)m,order?TableByteOrder_Big:TableByteOrder_Little,a); + if(!tail_intact(cook,m,sizeof(cook)))abort(); + if(!result){uint64_t magic=table_cook_read64(cook);if(magic==UINT64_C(0x4b4f4f434d484353)||magic==UINT64_C(0x5343484d434f4f4b))abort();goto done;} + }} + batch[0]=(const Root *)(const void *)b.region;batch[1]=batch[0]; + m=root_measure_messages_with_allocator(batch,2,&report,a);if(m<=0||m>4096)goto done; + memset(messages,0xa5,sizeof(messages));result=root_save_messages_with_allocator(batch,2,messages,m,&report,a); + if(!tail_intact(messages,m,sizeof(messages))){abort();}if(result!=m)goto done; + have_copy=1;if(!root_builder_init_with_allocator(©,a))goto done; + if(!root_load_builder(©,wire,n,&report) || !root_builder_lock(©))goto done; + if(node_at(NULL,&((const Root *)(const void *)copy.region)->first)!=node_at(NULL,&((const Root *)(const void *)copy.region)->second))goto done; + ok=1; + done: + if(have_copy)root_builder_shutdown(©); + root_builder_shutdown(&b);return ok; +} +int main(void) { + int fail,complete=0; + for(fail=0;fail<256;fail++) { + Counts counts={0,fail,0}; int ok=exercise(&counts); + if(counts.live!=0) { fprintf(stderr,"allocation %d leaked %d allocations\n",fail,counts.live);return 1; } + if(counts.calls<=fail) { if(!ok)return 2;complete=1;break; } + if(ok) { fprintf(stderr,"allocation %d was ignored\n",fail);return 3; } + } + return complete?0:4; +} +`) +} + +func TestCTableKeyedBounds(t *testing.T) { + u := unitFromSource(t, `package probe +enum Key { +First +Second +Third +} +table Root { slots [Key]uint32 } +`) + for _, release := range []bool{false, true} { + t.Run(fmt.Sprint(release), func(t *testing.T) { + prefix := "" + if release { + prefix = "#define NDEBUG\n" + } + runCTableWireProbe(t, u, prefix+`#include +static jmp_buf fatal; +#define schema_assert(x) ((void)(x)) +#define schema_fatal() longjmp(fatal,1) +#include "ProbeTable.h" +static void assign(Root * root, int32_t key) { SCHEMA_PROBE_ROOT_SLOTS_AT(*root,key)=17; } +int main(void) { + Root root; volatile int i; static const int32_t invalid[]={INT32_MIN,-1,0,KEY_MAX+1,INT32_MAX}; + root_reset(&root); + assign(&root,KEY_FIRST);assign(&root,KEY_THIRD); + if(root.slots[0]!=17 || root.slots[1]!=0 || root.slots[2]!=17) return 1; + {const Root * read=&root;int key=KEY_FIRST;Root * cursor=&root; + if(SCHEMA_PROBE_ROOT_SLOTS_AT(*read,key++)!=17||key!=KEY_SECOND)return 3; + SCHEMA_PROBE_ROOT_SLOTS_AT(*cursor++,KEY_SECOND)=23; + if(cursor!=&root+1||root.slots[1]!=23)return 4;} + + for(i=0;i<5;i++) { if(setjmp(fatal)==0) { assign(&root,invalid[i]); return 2; } } + return 0; +} +`) + }) + } +} + +func TestCTableBlobSizeRefusal(t *testing.T) { + u := unitFromSource(t, "package probe\ntable Root { data *bytes\ntext *string }\n") + runCTableWireProbe(t, u, `#include "ProbeTable.h" +int main(void) { + TableReport report={0}; TableReader body={0}; body.report=&report; + /* Framing premeasurement must reject the length without reading payload bytes. */ + body.size=(int64_t)UINT32_MAX+1; + if(table_node_wire_storage(&table_bytes_node_type,&body)!=-1 || report.reason!=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP) return 1; + report.reason=0; + if(table_node_wire_storage(&table_string_node_type,&body)!=-1 || report.reason!=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP) return 2; + body.size=UINT32_MAX;report.reason=0; + if(table_node_wire_storage(&table_bytes_node_type,&body)<=0 || report.reason) return 3; + return 0; +} +`) +} + +func TestCTableWireNestedLists(t *testing.T) { + u := unitFromSource(t, `package probe +table Row { numbers []int32 } +table Root { + rows []Row + nodes []*Row + tail *Row +} +`) + input := `{"rows":[{"numbers":[1,-2,3]},{"numbers":[]}],"nodes":[{"&node":1,"numbers":[7,8]},null,{"&node":1}],"tail":{"&node":1}}` + model := tabletext.NewModel(u) + root := model.New(u.Tables["Root"]) + report := tabletext.Report{} + if !model.Read(root, []byte(input), &report) || !report.Silent() { + t.Fatalf("oracle JSON: %+v", report) + } + expected, err := tablewire.Encode(model, root) + if err != nil { + t.Fatal(err) + } + var octets strings.Builder + for _, b := range expected { + fmt.Fprintf(&octets, "0x%02x,", b) + } + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do { if(!(x)) { fprintf(stderr,"line %%d: %%s\n",__LINE__,#x); return 1; } } while(0) +static const uint8_t expected[]={%s}; +int main(void) { + RootBuilder b,copy; Root * root; Row * row; int32_t * number; const Root * locked; const Root * loaded; + TableCtx ctx; TableSink sink; TableReport report={0}; int64_t bytes; uint8_t wire[sizeof(expected)],*region; + char text[4096]; int64_t text_bytes; + CHECK(root_builder_init(&b)); root=root_builder_root(&b); ctx.arena=&b.arena; sink.region=NULL;sink.worker=&b.main; + row=root_rows_add(&b.main,&root->rows); CHECK(row!=NULL); + number=row_numbers_add(&b.main,&row->numbers); CHECK(number!=NULL);*number=1; + number=row_numbers_add(&b.main,&row->numbers); CHECK(number!=NULL);*number=99; + CHECK(row_numbers_erase(&b.arena,&row->numbers,number)); CHECK(!row_numbers_erase(&b.arena,&row->numbers,number)); + number=row_numbers_add(&b.main,&row->numbers); CHECK(number!=NULL);*number=-2; + number=row_numbers_add(&b.main,&row->numbers); CHECK(number!=NULL);*number=3; + CHECK(root_rows_add(&b.main,&root->rows)!=NULL); + row=row_emplace(&sink,&root->tail); CHECK(row!=NULL); + number=row_numbers_add(&b.main,&row->numbers); CHECK(number!=NULL);*number=7; + number=row_numbers_add(&b.main,&row->numbers); CHECK(number!=NULL);*number=8; + *root_nodes_add(&b.main,&root->nodes)=root->tail; + CHECK(root_nodes_add(&b.main,&root->nodes)!=NULL); + *root_nodes_add(&b.main,&root->nodes)=root->tail; + CHECK(root_save(&ctx,root,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + CHECK(root_builder_lock(&b));locked=(const Root *)(const void *)b.region; + CHECK(root_nodes_at(&locked->nodes,0)==row_at(NULL,&locked->tail)); + CHECK(root_nodes_at(&locked->nodes,1)==NULL); + CHECK(*row_numbers_at(&root_rows_at(&locked->rows,0)->numbers,1)==-2); + CHECK(root_save(NULL,locked,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + bytes=root_load_measure(expected,sizeof(expected));CHECK(bytes==168); + region=(uint8_t *)malloc((size_t)bytes);CHECK(region!=NULL); + loaded=root_load(region,bytes,expected,sizeof(expected),&report);CHECK(loaded!=NULL && !report.malformed); + CHECK(root_nodes_at(&loaded->nodes,0)==row_at(NULL,&loaded->tail)); + CHECK(*row_numbers_at(&root_rows_at(&loaded->rows,0)->numbers,2)==3); + CHECK(root_save(NULL,loaded,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + CHECK(root_builder_init(©));CHECK(root_load_builder(©,expected,sizeof(expected),&report));CHECK(root_builder_lock(©)); + CHECK(root_save(NULL,(const Root *)(const void *)copy.region,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + root_builder_shutdown(©); + text_bytes=root_to_json(loaded,text,sizeof(text));CHECK(text_bytes>0); + CHECK(root_builder_init(©));CHECK(root_from_json(©,text,text_bytes,&report));CHECK(!report.malformed && !report.kind_mismatch);CHECK(root_builder_lock(©)); + CHECK(root_save(NULL,(const Root *)(const void *)copy.region,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + root_builder_shutdown(©);root_builder_shutdown(&b);free(region);return 0; +} +`, octets.String())) +} + +func TestCTableWireMaps(t *testing.T) { + u := unitFromSource(t, `package probe + table Row { numbers []int32 } + table Root { + numbers map[int32]int32 + rows map[string(16)]Row + links map[uint64]*Row + } + `) + input := `{"numbers":{"-1":8,"2":5},"rows":{"a":{"numbers":[]},"z":{"numbers":[7]}},"links":{"1":{"&node":1,"numbers":[9]},"18446744073709551615":{"&node":1}}}` + model := tabletext.NewModel(u) + root := model.New(u.Tables["Root"]) + report := tabletext.Report{} + if !model.Read(root, []byte(input), &report) || !report.Silent() { + t.Fatalf("oracle JSON: %+v", report) + } + expected, err := tablewire.Encode(model, root) + if err != nil { + t.Fatal(err) + } + var octets strings.Builder + for _, b := range expected { + fmt.Fprintf(&octets, "0x%02x,", b) + } + runCTableWireProbe(t, u, fmt.Sprintf(`#include "ProbeTable.h" +#include +#define CHECK(x) do { if(!(x)) { fprintf(stderr,"line %%d: %%s\n",__LINE__,#x); return 1; } } while(0) +static const uint8_t expected[]={%s}; +int main(void) { + RootBuilder b,copy; Root * root; Row * row; TableRef * ref; TableRef shared; + int32_t * value,*same; const Root * locked,*loaded; TableCtx ctx; TableSink sink; TableReport report={0}; + uint8_t wire[sizeof(expected)],*region; int64_t bytes; int32_t index_storage[16]; TableMapIndex index; char text[4096]; int64_t text_bytes; + CHECK(root_builder_init(&b));root=root_builder_root(&b);ctx.arena=&b.arena;sink.region=NULL;sink.worker=&b.main; + value=root_numbers_insert(&b.main,&root->numbers,2);CHECK(value!=NULL);*value=999; + same=root_numbers_insert(&b.main,&root->numbers,2);CHECK(same==value && *same==0);*same=5; + value=root_numbers_insert(&b.main,&root->numbers,-1);CHECK(value!=NULL);*value=8; + value=root_numbers_insert(&b.main,&root->numbers,3);CHECK(value!=NULL);*value=42; + CHECK(root_numbers_erase(&b.arena,&root->numbers,3));CHECK(!root_numbers_erase(&b.arena,&root->numbers,3)); + CHECK(*root_numbers_find_mut(&b.arena,&root->numbers,-1)==8); + row=root_rows_insert(&b.main,&root->rows,"z");CHECK(row!=NULL);value=row_numbers_add(&b.main,&row->numbers);CHECK(value!=NULL);*value=7; + CHECK(root_rows_insert(&b.main,&root->rows,"a")!=NULL); + ref=root_links_insert(&b.main,&root->links,UINT64_MAX);CHECK(ref!=NULL);row=row_emplace(&sink,ref);CHECK(row!=NULL);shared=*ref; + value=row_numbers_add(&b.main,&row->numbers);CHECK(value!=NULL);*value=9; + ref=root_links_insert(&b.main,&root->links,1);CHECK(ref!=NULL);*ref=shared; + CHECK(root_save(&ctx,root,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + CHECK(root_builder_lock(&b));locked=(const Root *)(const void *)b.region; + CHECK(*root_numbers_find(&locked->numbers,-1)==8 && root_numbers_find(&locked->numbers,3)==NULL); + CHECK(root_links_find(&locked->links,1)==root_links_find(&locked->links,UINT64_MAX)); + CHECK(*row_numbers_at(&root_rows_find(&locked->rows,"z")->numbers,0)==7); + index=root_numbers_index(&locked->numbers,index_storage,sizeof(index_storage));CHECK(index.good); + CHECK(root_numbers_index_find(&index,&locked->numbers,2)==root_numbers_find(&locked->numbers,2)); + CHECK(root_numbers_index_find(&index,&locked->numbers,3)==NULL); + CHECK(root_save(NULL,locked,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + bytes=root_load_measure(expected,sizeof(expected));CHECK(bytes>0);region=(uint8_t *)malloc((size_t)bytes);CHECK(region!=NULL); + loaded=root_load(region,bytes,expected,sizeof(expected),&report);CHECK(loaded!=NULL && !report.malformed && !report.kind_mismatch); + CHECK(root_links_find(&loaded->links,1)==root_links_find(&loaded->links,UINT64_MAX)); + CHECK(root_save(NULL,loaded,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + CHECK(root_builder_init(©));CHECK(root_load_builder(©,expected,sizeof(expected),&report));CHECK(root_builder_lock(©)); + CHECK(root_save(NULL,(const Root *)(const void *)copy.region,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + root_builder_shutdown(©); + text_bytes=root_to_json(loaded,text,sizeof(text));CHECK(text_bytes>0); + CHECK(root_builder_init(©));CHECK(root_from_json(©,text,text_bytes,&report));CHECK(!report.malformed && !report.kind_mismatch);CHECK(root_builder_lock(©)); + CHECK(root_save(NULL,(const Root *)(const void *)copy.region,wire,sizeof(wire))==(int64_t)sizeof(expected));CHECK(!memcmp(wire,expected,sizeof(expected))); + root_builder_shutdown(©);root_builder_shutdown(&b);free(region);return 0; +} +`, octets.String())) +} + +// A generated map entry is storage reached through its owning map, never a root. +func TestCTableMapEntryNotRoot(t *testing.T) { + files, err := New().Generate(unitFromSource(t, mapSrc), "c", Options{}) + if err != nil { + t.Fatal(err) + } + var headers strings.Builder + for name, data := range files { + if strings.HasSuffix(name, "Table.h") { + headers.Write(data) + } + } + text := headers.String() + for _, entry := range []struct{ typ, api string }{ + {"FleetShipsEntry", "fleet_ships_entry"}, {"FleetByIdEntry", "fleet_by_id_entry"}, + {"FleetLoadoutsEntry", "fleet_loadouts_entry"}, {"FleetLoadoutsEntryValueEntry", "fleet_loadouts_entry_value_entry"}, + } { + if !strings.Contains(text, "struct "+entry.typ) { + t.Errorf("entry storage %s missing", entry.typ) + } + if strings.Contains(text, "struct "+entry.typ+"Builder") { + t.Errorf("map entry %s has a builder", entry.typ) + } + for _, verb := range []string{"open", "cook", "save", "load", "measure", "from_json", "to_json"} { + if strings.Contains(text, entry.api+"_"+verb+"(") || strings.Contains(text, entry.api+"_"+verb+" (") { + t.Errorf("map entry %s has root API %s", entry.typ, verb) + } + } + } +} + +func TestCTableMapWideningCount(t *testing.T) { + source := unitFromSource(t, "package probe\ntable Root { entries map[int8]int8 }\n") + target := unitFromSource(t, "package probe\ntable Root { entries map[int32]int32 }\n") + m := tabletext.NewModel(source) + v := m.New(source.Tables["Root"]) + var report tabletext.Report + if !m.Read(v, []byte(`{"entries":{"-3":-5,"2":9}}`), &report) || !report.Silent() { + t.Fatal(report) + } + wire, err := tablewire.Encode(m, v) + if err != nil { + t.Fatal(err) + } + m = tabletext.NewModel(target) + v = m.New(target.Tables["Root"]) + if ok, err := tablewire.Decode(m, v, wire, &report); err != nil || !ok || report.Widened != 3 { + t.Fatalf("oracle: %+v %v", report, err) + } + want, err := tablewire.Encode(m, v) + if err != nil { + t.Fatal(err) + } + runCTableWireProbe(t, target, fmt.Sprintf(`#include "ProbeTable.h" +#include +#include +#include +#define CHECK(x) do{if(!(x)){fprintf(stderr,"map widening line %%d: %%s\n",__LINE__,#x);return 1;}}while(0) +int main(void){const uint8_t wire[]={%s},want[]={%s};TableReport report={0};int64_t n=root_load_measure(wire,sizeof(wire));CHECK(n>0);uint8_t * region=(uint8_t*)malloc((size_t)n);const Root * root=root_load(region,n,wire,sizeof(wire),&report);CHECK(root);CHECK(report.widened==3);CHECK(!report.malformed&&!report.kind_mismatch);uint8_t out[sizeof(want)];CHECK(root_save(NULL,root,out,sizeof(out))==sizeof(out));CHECK(memcmp(out,want,sizeof(out))==0);free(region);return 0;} +`, cppWire(wire), cppWire(want))) +} diff --git a/compiler/tableslists.go b/compiler/tableslists.go index 126560a40..927482217 100644 --- a/compiler/tableslists.go +++ b/compiler/tableslists.go @@ -7,6 +7,7 @@ package compiler import ( "fmt" + "slices" "github.com/mas-bandwidth/schema/v2/ir" ) @@ -20,16 +21,12 @@ var listTargets []string // beside its registerBuiltin call. func registerListCarrier(name string) { listTargets = append(listTargets, name) } -// refuseLists is the named refusal every PORT gives a unit whose table -// closure declares a `[]T` (docs/SPEC-TABLES.md §2.9, §11, §15). -// -// An unbounded array is a VARIABLE-CLASS construct, and the variable class is -// the C++ reference's alone — the arena, the region, the node extent and the -// walks a list's elements ride in are all the reference's. So the reference -// carries the codec, registers through [registerListCarrier] from its own -// init and never reaches here, and every port refuses loudly rather than -// emitting a codec that never met an element array. +// refuseLists gives targets without this codec a named refusal. +// Registered carriers accept the construct. func refuseLists(u *ir.Unit, target string) error { + if slices.Contains(listTargets, target) { + return nil + } fields := ir.ListFields(u) if len(fields) == 0 { return nil diff --git a/compiler/tableslists_test.go b/compiler/tableslists_test.go index 639b3ac3c..d58ef661b 100644 --- a/compiler/tableslists_test.go +++ b/compiler/tableslists_test.go @@ -1,10 +1,11 @@ package compiler // The UNBOUNDED ARRAY's cross-target refusals (docs/SPEC-TABLES.md §2.9, §11, -// §15): the C++ reference carries the codec, every port refuses a unit that -// declares one BY NAME, and none of them refuses a list-free unit for it. +// §15): C and C++ carry the codec. Other targets refuse a unit that declares +// one by name, and none refuses a list-free unit for it. import ( + "slices" "strings" "testing" @@ -25,18 +26,16 @@ table Save } ` -// TestListsAreRefusedByEveryPort: the refusal is a REFUSAL and not a silent -// emission of an array whose elements no port laid out, and the reference -// does not refuse. -func TestListsAreRefusedByEveryPort(t *testing.T) { +// TestListsAreRefusedByNonCarriers prevents silent emission without a codec. +func TestListsAreRefusedByNonCarriers(t *testing.T) { u := unitFromSource(t, listSrc) c := New() for _, target := range c.Targets() { t.Run(target, func(t *testing.T) { _, err := c.Generate(u, target, Options{}) - if target == "cpp" || target == "cs" || target == "go" { + if slices.Contains(listTargets, target) { if err != nil { - t.Fatalf("--lang cpp refused an unbounded array: the reference carries the codec (schema#531): %v", err) + t.Fatalf("--lang %s refused an unbounded array: %v", target, err) } return } @@ -52,11 +51,10 @@ func TestListsAreRefusedByEveryPort(t *testing.T) { } } -// TestListCarriers pins the targets that implement the list codec -// (docs/SPEC-TABLES.md §2.9, §15). +// TestListCarriers keeps the advertised targets in step with their codecs. func TestListCarriers(t *testing.T) { - if len(listTargets) != 3 || listTargets[0] != "cpp" || listTargets[1] != "cs" || listTargets[2] != "go" { - t.Fatalf("listTargets = %v, want exactly [cpp cs go]: the variable class is the reference's (docs/SPEC-TABLES.md §2.9, §15)", listTargets) + if !slices.Equal(listTargets, []string{"c", "cpp", "cs", "go"}) { + t.Fatalf("listTargets = %v, want [c cpp cs go]", listTargets) } } @@ -93,11 +91,11 @@ func TestListFieldsNamesWhatAnAuthorWrote(t *testing.T) { // TestListRefusalNamesTheCarrier: what a port's refusal says: the carrier, // the flag that generates, and the fields an author wrote. func TestListRefusalNamesTheCarrier(t *testing.T) { - err := refuseLists(unitFromSource(t, listSrc), "cs") + err := refuseLists(unitFromSource(t, listSrc), "rust") if err == nil { t.Fatalf("refuseLists accepted a list-bearing unit for a non-carrier") } - for _, want := range []string{"cpp, cs and go", "Save.placements", "--lang cpp"} { + for _, want := range []string{"a []T is c, cpp, cs and go only today", "Save.placements", "--lang cpp"} { if !strings.Contains(err.Error(), want) { t.Errorf("the carrier-form refusal does not name %q: %v", want, err) } diff --git a/compiler/tablesmaps.go b/compiler/tablesmaps.go index 7a13a63f4..8fe17f4a4 100644 --- a/compiler/tablesmaps.go +++ b/compiler/tablesmaps.go @@ -7,6 +7,7 @@ package compiler import ( "fmt" + "slices" "github.com/mas-bandwidth/schema/v2/ir" ) @@ -19,16 +20,12 @@ var mapTargets []string // beside its registerBuiltin call. func registerMapCarrier(name string) { mapTargets = append(mapTargets, name) } -// refuseMaps is the named refusal every PORT gives a unit whose table closure -// declares a MAP (docs/SPEC-TABLES.md §2.8, §11, §15). -// -// A map is a VARIABLE-CLASS construct, and the variable class is the C++ -// reference's alone — the arena, the region, the node extent and the walks a -// map's entries ride in are all the reference's. So the reference carries the -// codec, registers through [registerMapCarrier] from its own init and never -// reaches here, and every port refuses loudly rather than emitting a codec -// that never met the entry, its sort or its ascending check. +// refuseMaps gives targets without this codec a named refusal. +// Registered carriers accept the construct. func refuseMaps(u *ir.Unit, target string) error { + if slices.Contains(mapTargets, target) { + return nil + } fields := ir.MapFields(u) if len(fields) == 0 { return nil diff --git a/compiler/tablesmaps_test.go b/compiler/tablesmaps_test.go index b6fbf74a3..f107d64fd 100644 --- a/compiler/tablesmaps_test.go +++ b/compiler/tablesmaps_test.go @@ -6,6 +6,7 @@ package compiler import ( + "slices" "strings" "testing" @@ -132,21 +133,17 @@ func TestMapsAreLegal(t *testing.T) { } } -// TestMapsAreRefusedByNonCarriers: the C++ REFERENCE carries the codec and the -// eight ports do not (docs/SPEC-TABLES.md §2.8, §15) — a map is a -// variable-class construct and the variable class is the reference's alone. -// Every port refuses the UNIT, naming the fields, naming the carrier and -// naming the flag that generates. A codec that never met the entry, its sort -// or its ascending check must not be emitted anywhere. +// TestMapsAreRefusedByNonCarriers requires a named refusal until a target +// carries map storage, sorting, and its ascending-key reader check. func TestMapsAreRefusedByNonCarriers(t *testing.T) { u := unitFromSource(t, mapSrc) c := New() for _, target := range c.Targets() { t.Run(target, func(t *testing.T) { _, err := c.Generate(u, target, Options{}) - if target == "cpp" || target == "cs" || target == "go" { + if slices.Contains(mapTargets, target) { if err != nil { - t.Fatalf("--lang %s refused a supported map: %v", target, err) + t.Fatalf("--lang %s refused a map: %v", target, err) } return } @@ -162,13 +159,10 @@ func TestMapsAreRefusedByNonCarriers(t *testing.T) { } } -// TestMapCarriers pins the implemented map codecs (docs/SPEC-TABLES.md -// §2.8, §15). A port that -// registers here without its codec would turn every refusal below into a -// silent acceptance. +// TestMapCarriers keeps the advertised targets in step with their codecs. func TestMapCarriers(t *testing.T) { - if len(mapTargets) != 3 || mapTargets[0] != "cpp" || mapTargets[1] != "cs" || mapTargets[2] != "go" { - t.Fatalf("mapTargets = %v, want exactly [cpp cs go] — the variable class is the reference's (docs/SPEC-TABLES.md §2.8, §15)", mapTargets) + if !slices.Equal(mapTargets, []string{"c", "cpp", "cs", "go"}) { + t.Fatalf("mapTargets = %v, want [c cpp cs go]", mapTargets) } } @@ -193,17 +187,16 @@ func TestMapFreeUnitIsUntouched(t *testing.T) { // the fields an author wrote. func TestMapRefusalNamesTheCarrier(t *testing.T) { u := unitFromSource(t, mapSrc) - err := refuseMaps(u, "cs") + err := refuseMaps(u, "rust") if err == nil { t.Fatalf("refuseMaps accepted a map-bearing unit for a non-carrier") } - for _, want := range []string{"a map is cpp, cs and go only today", "Fleet.ships", "--lang cpp"} { + for _, want := range []string{"a map is c, cpp, cs and go only today", "Fleet.ships", "--lang cpp"} { if !strings.Contains(err.Error(), want) { t.Errorf("the carrier-form refusal does not name %q: %v", want, err) } } - // a CARRIER never calls refuseMaps at all — it registers instead, exactly as - // target_cpp.go does for the optional array — so there is no third shape. + // Registered carriers pass through refuseMaps without a refusal. } // TestMapTypeSpelling: a diagnostic and a descriptor name the declaration the diff --git a/compiler/tablesoptionalarrays.go b/compiler/tablesoptionalarrays.go index 91ae6fe56..ffedfd378 100644 --- a/compiler/tablesoptionalarrays.go +++ b/compiler/tablesoptionalarrays.go @@ -8,6 +8,7 @@ package compiler import ( "fmt" + "slices" "github.com/mas-bandwidth/schema/v2/ir" ) @@ -29,6 +30,9 @@ func registerOptionalArrayCarrier(name string) { // is a named follow-on — refused loudly here rather than emitted as a // fixed-class codec that never met the presence companion beside an array. func refuseOptionalArrays(u *ir.Unit, target string) error { + if slices.Contains(optionalArrayTargets, target) { + return nil + } fields := ir.TableOptionalArrays(u) if len(fields) == 0 { return nil diff --git a/compiler/tablesoptionalarrays_test.go b/compiler/tablesoptionalarrays_test.go index 496bb7822..7ca36470d 100644 --- a/compiler/tablesoptionalarrays_test.go +++ b/compiler/tablesoptionalarrays_test.go @@ -1,4 +1,4 @@ -// The cross-target gate on optional arrays: C++ and C# carry the fixed +// The cross-target gate on optional arrays: C++, C and C# carry the fixed // shapes, and targets without them refuse the unit by name. package compiler @@ -88,7 +88,7 @@ func TestOptionalArraysCarriers(t *testing.T) { u := unitFromSource(t, optionalArraySrc) c := New() for _, target := range c.Targets() { - if target == "cpp" || target == "cs" || target == "go" { + if target == "cpp" || target == "c" || target == "cs" || target == "go" { files, err := c.Generate(u, target, Options{}) if err != nil { t.Fatalf("--lang %s refused the supported shape: %v", target, err) diff --git a/compiler/tablesunionarrays_test.go b/compiler/tablesunionarrays_test.go index 95819e3c4..90c9d4b8e 100644 --- a/compiler/tablesunionarrays_test.go +++ b/compiler/tablesunionarrays_test.go @@ -1,4 +1,4 @@ -// The cross-target gate on arrays of unions: C++ and C# carry the fixed +// The cross-target gate on arrays of unions: C++, C and C# carry the fixed // shapes, and targets without them refuse the unit by name. package compiler @@ -43,7 +43,7 @@ func TestUnionArraysCarriers(t *testing.T) { u := unitFromSource(t, unionArraySrc) c := New() for _, target := range c.Targets() { - if target == "cpp" || target == "cs" || target == "go" { + if target == "cpp" || target == "c" || target == "cs" || target == "go" { files, err := c.Generate(u, target, Options{}) if err != nil { t.Fatalf("--lang %s refused the supported shape: %v", target, err) diff --git a/compiler/target_c.go b/compiler/target_c.go index e9511adf7..cec9247a4 100644 --- a/compiler/target_c.go +++ b/compiler/target_c.go @@ -16,7 +16,7 @@ type cTarget struct{} func (cTarget) Names() []string { return []string{"c"} } func (cTarget) Generate(u *ir.Unit, _ Options) (map[string][]byte, error) { - // Packet wide text is carried; table kind 33 remains a named refusal. + // C carries packet and table wide text. if err := refuseWideText(u, "c"); err != nil { return nil, err } @@ -54,6 +54,9 @@ func (cTarget) Generate(u *ir.Unit, _ Options) (map[string][]byte, error) { func init() { registerWideTextCarrier("c") - registerBuiltin(cTarget{}, true, false, false, false) + registerBuiltin(cTarget{}, true, true, true, true) + registerOptionalArrayCarrier("c") + registerListCarrier("c") + registerMapCarrier("c") registerPacketValueDefaultCarrier("c") } diff --git a/compiler/valuedefaults.go b/compiler/valuedefaults.go index dd466bc65..a1f7ea699 100644 --- a/compiler/valuedefaults.go +++ b/compiler/valuedefaults.go @@ -14,7 +14,7 @@ import ( // valueDefaultTargets is the canonical name of every built-in target whose // backends carry a string, bytes or flags default on both wires. -var valueDefaultTargets = []string{"cpp", "cs"} +var valueDefaultTargets = []string{"cpp", "c", "cs"} // packetValueDefaultTargets names the packet carriers independently of the // table carriers. A port registers here from its own target file's init. diff --git a/compiler/valuedefaults_test.go b/compiler/valuedefaults_test.go index ae1b0a0c8..4dc48a12a 100644 --- a/compiler/valuedefaults_test.go +++ b/compiler/valuedefaults_test.go @@ -1,5 +1,5 @@ // A string, bytes or flags default (SPEC §4.2) has separate packet and table -// carriers. C++ and Go carry both; the remaining packet carriers refuse table defaults. +// carriers. C, C++, C# and Go carry both; other targets carry packet-only defaults. package compiler import ( @@ -42,19 +42,14 @@ func TestTableValueDefaultsCarriers(t *testing.T) { } continue } - if target == "go" { + if target == "c" { if err != nil { - t.Fatalf("go refused supported table defaults: %v", err) - } - fixed := unitFromSource(t, strings.ReplaceAll(valueDefaultsUnit, "flagship *Ship", "flagship Ship")) - goFiles, err := c.Generate(fixed, target, Options{}) - if err != nil { - t.Fatal(err) + t.Fatalf("C refused table defaults: %v", err) } - code := string(goFiles["ProbeTable.go"]) - for _, want := range []string{`copy(value.Label[:], "new")`, `copy(value.Name[:], "untitled")`, `copy(value.Tag[:], "ab")`, `value.Caps = 3`} { - if !strings.Contains(code, want) { - t.Errorf("go default output lacks %q", want) + header := string(out["ProbeTable.h"]) + for _, want := range []string{"value->name_length = 8;", "value->tag_length = 2;", "value->caps = 3ull;"} { + if !strings.Contains(header, want) { + t.Errorf("C output lacks %q", want) } } continue @@ -78,6 +73,23 @@ func TestTableValueDefaultsCarriers(t *testing.T) { } continue } + if target == "go" { + if err != nil { + t.Fatalf("go refused supported table defaults: %v", err) + } + fixed := unitFromSource(t, strings.ReplaceAll(valueDefaultsUnit, "flagship *Ship", "flagship Ship")) + goFiles, err := c.Generate(fixed, target, Options{}) + if err != nil { + t.Fatal(err) + } + code := string(goFiles["ProbeTable.go"]) + for _, want := range []string{`copy(value.Label[:], "new")`, `copy(value.Name[:], "untitled")`, `copy(value.Tag[:], "ab")`, `value.Caps = 3`} { + if !strings.Contains(code, want) { + t.Errorf("go default output lacks %q", want) + } + } + continue + } if err == nil { t.Errorf("%s emitted a unit with string, bytes and flags defaults instead of refusing it", target) continue diff --git a/compiler/wasrows.go b/compiler/wasrows.go index 69acda108..f6ecfe539 100644 --- a/compiler/wasrows.go +++ b/compiler/wasrows.go @@ -15,7 +15,7 @@ import ( // wasRowTargets is the canonical name of every built-in target whose backends // carry the three; refuseWasRows names them. -var wasRowTargets = []string{"cpp", "cs"} +var wasRowTargets = []string{"cpp", "c", "cs"} // refuseWasRows is the named refusal every target without the form gives a // unit whose table closure carries a variant, arm or type-field `was`. diff --git a/compiler/wasrows_test.go b/compiler/wasrows_test.go index 60ab4cf6e..ebd8e9c0f 100644 --- a/compiler/wasrows_test.go +++ b/compiler/wasrows_test.go @@ -1,5 +1,5 @@ // A `was` on an enum variant, a union arm or a type's field (docs/SPEC-TABLES.md -// §5) is carried by C++ and C#; targets without the form refuse it by name. +// §5) is carried by C++, C and C#; targets without the form refuse it by name. package compiler import ( @@ -49,7 +49,7 @@ func TestWasRowsCarriers(t *testing.T) { c := New() for _, target := range c.Targets() { out, err := c.Generate(u, target, Options{}) - if target == "cpp" || target == "cs" || target == "go" { + if target == "cpp" || target == "c" || target == "cs" || target == "go" { if err != nil { t.Fatalf("%s carries the was rows and refused: %v", target, err) } diff --git a/compiler/widetext.go b/compiler/widetext.go index 554eb1799..7ed7ee085 100644 --- a/compiler/widetext.go +++ b/compiler/widetext.go @@ -53,8 +53,8 @@ func refuseWideText(u *ir.Unit, target string) error { tableFields = append(tableFields, field) } } - if len(tableFields) > 0 && target != "cpp" && target != "cs" && target != "go" { - return fmt.Errorf("unit puts a wstring(N) field in a table closure (%s): table wide text is C++, C# and Go only today, and the %s table codec is a named follow-on; generate with --lang cpp, --lang cs or --lang go (SPEC §4.12)", englishList(tableFields), target) + if len(tableFields) > 0 && target != "cpp" && target != "c" && target != "cs" && target != "go" { + return fmt.Errorf("unit puts a wstring(N) field in a table closure (%s): table wide text is C, C++, C# and Go only today, and the %s table codec is a named follow-on; generate with --lang cpp (SPEC §4.12)", englishList(tableFields), target) } if slices.Contains(wideTextTargets, target) { return nil diff --git a/compiler/widetext_test.go b/compiler/widetext_test.go index 34da23895..874d0fcdf 100644 --- a/compiler/widetext_test.go +++ b/compiler/widetext_test.go @@ -79,7 +79,7 @@ func TestWideTextInATableClosureRefusesMissingCarriers(t *testing.T) { if err != nil { t.Fatalf("Load refused a wstring(N) inside a table closure: %v", err) } - for _, target := range []string{"c", "rust", "java", "js", "dart", "elixir"} { + for _, target := range []string{"rust", "java", "js", "dart", "elixir"} { _, err := c.Generate(u, target, Options{}) if err == nil { t.Errorf("%s took a wstring(N) it does not carry", target) diff --git a/docs/PORTING.md b/docs/PORTING.md index 4992ba308..074a1bf03 100644 --- a/docs/PORTING.md +++ b/docs/PORTING.md @@ -273,7 +273,7 @@ enum answered `undefined` before the guard was made symmetric. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-keyed-none-refusal-ndebug` `tables-keyed-max-refusal-ndebug` | ❌ #377 (None refused; past Max is an unchecked plain array) | ✅ `internal/codegen/rusttable/runtime.go:133-169` (panics in every build) | ✅ `TestKeyedRefusesNone` `TestKeyedRefusesPastMax` `TestKeyedPlacesByKey` | ✅ `test/cs-tables/src/Program.cs:1672` (None refused; past Max is the CLR's, always on) | ❌ #407 (refuses both ends; no test holds it) | ✅ `tables-js-keyed-negative-control` | ❌ #407 (refuses both ends; no test holds it) | ❌ #407 (guards refuse both ends; no test holds it) | +| ✅ `tables-keyed-none-refusal-ndebug` `tables-keyed-max-refusal-ndebug` | ✅ `TestCTableKeyedBounds` `tables-c-keyed-none-refusal-negative-control` | ✅ `internal/codegen/rusttable/runtime.go:133-169` (panics in every build) | ✅ `TestKeyedRefusesNone` `TestKeyedRefusesPastMax` `TestKeyedPlacesByKey` | ✅ `test/cs-tables/src/Program.cs:1672` (None refused; past Max is the CLR's, always on) | ❌ #407 (refuses both ends; no test holds it) | ✅ `tables-js-keyed-negative-control` | ❌ #407 (refuses both ends; no test holds it) | ❌ #407 (guards refuse both ends; no test holds it) | ### M6 — The variable class on the wire: a flat node table and an identity map @@ -306,7 +306,7 @@ in post-order in the emitter and requires the lock red; | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-flat-wire` `tables-flat-wire-negative-control` | ❌ #408 (the earlier nested form: a depth cap, no identity map) | ❌ #349 | ✅ `TestRegionGraphs` `tables-go-wire-fuzz` `tables-go-wire-fuzz-negative-control` | ✅ `tables-cs-leg` `tables-cs-region-fuzz` (flat native and managed graphs) | ❌ #349 | ❌ #349 | ❌ #349 | ❌ #349 | +| ✅ `tables-flat-wire` `tables-flat-wire-negative-control` | ✅ `TestCTableWireGraphIdentity` `tables-c-wire-fuzz` `tables-c-wire-fuzz-negative-control` | ❌ #349 | ✅ `TestRegionGraphs` `tables-go-wire-fuzz` `tables-go-wire-fuzz-negative-control` | ✅ `tables-cs-leg` `tables-cs-region-fuzz` (flat native and managed graphs) | ❌ #349 | ❌ #349 | ❌ #349 | ❌ #349 | ### M7 — A block row is reached by stride, with no per-row object @@ -435,7 +435,7 @@ original-slice guarantee, bypass the pair, and add a managed allocation. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-hooks` | ❌ #410 (raw `calloc`/`free` on the pointer path) | — no allocating path exists: a pointered unit's wire is refused and the fixed class allocates nothing (`tables-rust-alloc-audit`) | ✅ `tables-go-allocator` `tables-go-allocator-negative-controls` (typed activation frames stated below) | ✅ `tables-cs-leg` (`TableAllocator` pairs, growth failures and native scratch ownership) | — the runtime allocates inside itself and says so; where it does is named per path at `tables-java-alloc` | — the runtime allocates inside itself and says so; every unavoidable allocation is named in the floor (docs/SPEC-TABLES.md) | — the runtime allocates inside itself and says so (docs/SPEC-TABLES.md) | — the BEAM allocates every term; the count is pinned instead (docs/SPEC-TABLES.md) | +| ✅ `tables-hooks` | ✅ `TestCTableWireAllocatorFailures` `tables-c-soak` | — no allocating path exists: a pointered unit's wire is refused and the fixed class allocates nothing (`tables-rust-alloc-audit`) | ✅ `tables-go-allocator` `tables-go-allocator-negative-controls` (typed activation frames stated below) | ✅ `tables-cs-leg` (`TableAllocator` pairs, growth failures and native scratch ownership) | — the runtime allocates inside itself and says so; where it does is named per path at `tables-java-alloc` | — the runtime allocates inside itself and says so; every unavoidable allocation is named in the floor (docs/SPEC-TABLES.md) | — the runtime allocates inside itself and says so (docs/SPEC-TABLES.md) | — the BEAM allocates every term; the count is pinned instead (docs/SPEC-TABLES.md) | ### M11 — The layout contract is asserted in generated code @@ -537,7 +537,7 @@ adapters; `tables-json-graph-walk`. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-json-graph-walk` | ❌ #408 | ❌ #349 | ✅ `TestRegionGraphs` `tables-go-json-walk` | ✅ `tables-cs-leg` (shared graph labels and forward references) | ❌ #349 | ❌ #349 | ❌ #349 | ❌ #349 | +| ✅ `tables-json-graph-walk` | ✅ `TestCTableWireGraphIdentity` `TestCTableWireBlobIdentity` | ❌ #349 | ✅ `TestRegionGraphs` `tables-go-json-walk` | ✅ `tables-cs-leg` (shared graph labels and forward references) | ❌ #349 | ❌ #349 | ❌ #349 | ❌ #349 | ### M15 — One walk for the numbering, the pack measure and the pack @@ -578,7 +578,7 @@ post-order numbering the same way. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `internal/codegen/cpptable/pointers.go:150` `conformance` | ❌ #433 (its `pack_measure` and `pack` take every pointer field before every by-value nesting, `internal/codegen/ctable/pointers.go:220`, `:280`) | ❌ #349 | ✅ `internal/codegen/gotable/regions.go` (`tableRegionEdges`, `tableNumber`) `TestAllocatorOwnershipAndStandaloneWriters` `tables-go-builders` | ✅ `tables-cs-pack-negative-control` `tables-cs-leg` (root and node extent equality) | ❌ #349 | ❌ #349 | ❌ #349 | ❌ #349 | +| ✅ `internal/codegen/cpptable/pointers.go:150` `conformance` | ✅ `internal/codegen/ctable/graph.go:24` `TestCTableCookCanonical` `tables-c-wire-fuzz` | ❌ #349 | ✅ `internal/codegen/gotable/regions.go` (`tableRegionEdges`, `tableNumber`) `TestAllocatorOwnershipAndStandaloneWriters` `tables-go-builders` | ✅ `tables-cs-pack-negative-control` `tables-cs-leg` (root and node extent equality) | ❌ #349 | ❌ #349 | ❌ #349 | ❌ #349 | ### M16 — The presence companion rides beside the array walks @@ -619,7 +619,7 @@ on the corpus rows that carry it. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `internal/codegen/cpptable/codecs.go:588` `test/tables/main.cpp:6814` `TestReportRowsDecodeThroughTheEngine` | ❌ #392 | ❌ #392 | ✅ `TestDefaultsAndOptionalArrays` `conformance` | ✅ `tables-cs-leg` (optional arrays across file, message and cook walks) | ❌ #392 | ❌ #392 | ❌ #392 | ❌ #392 | +| ✅ `internal/codegen/cpptable/codecs.go:588` `test/tables/main.cpp:6814` `TestReportRowsDecodeThroughTheEngine` | ✅ `TestCTableWireDefaultRecovery` `tables-c-wire-fuzz` | ❌ #392 | ✅ `TestDefaultsAndOptionalArrays` `conformance` | ✅ `tables-cs-leg` (optional arrays across file, message and cook walks) | ❌ #392 | ❌ #392 | ❌ #392 | ❌ #392 | ### M17 — A node larger than a slab takes a span of the address space **Method.** An arena hands out nodes by bumping inside a fixed-size slab, and a @@ -655,7 +655,7 @@ reads it back after the nodes allocated behind it and the bytes are gone. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-blob-span-negative-control` | ❌ #259 | ❌ #259 | ❌ #259 | ✅ `tables-cs-leg` (large spans, descending addresses and failed blob allocation) | ❌ #259 | ❌ #259 | ❌ #259 | ❌ #259 | +| ✅ `tables-blob-span-negative-control` | ✅ `internal/codegen/ctable/arena.go:312` `TestCTableWireBlobIdentity` | ❌ #259 | ❌ #259 | ✅ `tables-cs-leg` (large spans, descending addresses and failed blob allocation) | ❌ #259 | ❌ #259 | ❌ #259 | ❌ #259 | ### M18 — A union arm is a field line @@ -709,7 +709,7 @@ payload's last byte, decodes a body that ends inside its own length. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `internal/codegen/cpptable/arms.go` `tables-wire-fuzz-arm-width-negative-control` `tables-wire-fuzz-arm-terminator-negative-control` | ❌ #392 | ❌ #392 | ✅ `TestRegionUnionAndBlobValues` `TestUnionPointerArrayReaders` `tables-go-measure-negative-controls` `tables-go-wire-fuzz` | ✅ `tables-cs-leg` `conformance-negative-control-cs` (all field-shaped arms) | ❌ #392 | ❌ #392 | ❌ #392 | ❌ #392 | +| ✅ `internal/codegen/cpptable/arms.go` `tables-wire-fuzz-arm-width-negative-control` `tables-wire-fuzz-arm-terminator-negative-control` | ✅ `tables-c-collections-fuzz` `tables-c-wire-fuzz` | ❌ #392 | ✅ `TestRegionUnionAndBlobValues` `TestUnionPointerArrayReaders` `tables-go-measure-negative-controls` `tables-go-wire-fuzz` | ✅ `tables-cs-leg` `conformance-negative-control-cs` (all field-shaped arms) | ❌ #392 | ❌ #392 | ❌ #392 | ❌ #392 | C# carries the same field-shaped arm descriptors through file, message, JSON, native region and cook walks. `tables-cs-leg` and the three ordinary read @@ -783,7 +783,7 @@ shared control. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-maps` `tables-json-map-walk` `tables-maps-negative-controls` `tables-maps-cook-check-negative-control`, and the TOOL's wire and text halves and its cook-check clause (`TestTheToolWritesTheReferencesMapBytes`, `TestCookCheckMapSlot`) | ❌ #502 | ❌ #502 | ✅ `tables-go-containers` `tables-go-containers-negative-controls` `tables-go-wire-fuzz` | ✅ `tables-cs-leg` `tables-cs-wire-fuzz` `tables-cs-builder-fuzz` (stable sorted entries and native holder extents) | ❌ #502 | ❌ #502 | ❌ #502 | ❌ #502 | +| ✅ `tables-maps` `tables-json-map-walk` `tables-maps-negative-controls` `tables-maps-cook-check-negative-control`, and the TOOL's wire and text halves and its cook-check clause (`TestTheToolWritesTheReferencesMapBytes`, `TestCookCheckMapSlot`) | ✅ `TestCTableWireMaps` `TestCTableMapWideningCount` `tables-c-collections-fuzz` | ❌ #502 | ✅ `tables-go-containers` `tables-go-containers-negative-controls` `tables-go-wire-fuzz` | ✅ `tables-cs-leg` `tables-cs-wire-fuzz` `tables-cs-builder-fuzz` (stable sorted entries and native holder extents) | ❌ #502 | ❌ #502 | ❌ #502 | ❌ #502 | ### M20 — The id-table wire @@ -827,10 +827,15 @@ three, the message overload of `LoadMeasure` for a pointered batch's one region, `TableVocabulary` and the three unit-scope entry points `Announce`, `AnnounceMeasure` and `AnnounceRead`, each in that language's own naming convention. **The verbs are PLURAL** because the form's primitive is a batch of -bodies of one root and a single message is the batch of one. C++ and the tool -carry a form-`2` path today, byte framed until the codec change lands §3.3, and -the harness's `message` surface prints ABSENT for every port, so the cell is -where the work is counted. The BODY's rules are the ones a port already has, +bodies of one root and a single message is the batch of one. C++, C and the +independent tool engine carry the bitpacked form-`2` path. The C conformance +and mutation drivers exercise fixed and variable message roots. Retained +unknowns use caller-owned byte and identity stores. Replacing an occurrence +scans the retained-record log, whose size is bounded by the caller's storage. +`tables-c-retain` checks +file/message load retention and retained file saves, and +`tables-c-retain-negative-control` drops a field and fails the public report. The BODY's +rules are the ones a port already has, read off a bit stream instead of a byte one: references resolve against the announced vocabulary instead of a trailer, elision and every tolerance rule above are unchanged, and the two rules that DO move are named on the page, @@ -859,7 +864,7 @@ through `go build -overlay` and each turning the fuzzer red on its own verdict. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-wire-fuzz` `tables-wire-fuzz-negative-control` | ❌ #512 | ❌ #518 | ✅ `tables-go-wire-fuzz` `tables-go-wire-fuzz-negative-control` `tables-go-retain-wire-fuzz` (fixed and variable file/message forms) | ✅ `tables-cs-wire-fuzz` `tables-cs-region-fuzz` `tables-cs-retain-fuzz` `conformance-negative-control-cs` | ❌ #517 | ❌ #516 | ❌ #514 | ❌ #515 | +| ✅ `tables-wire-fuzz` `tables-wire-fuzz-negative-control` | ✅ file and message forms: `tables-c-wire-fuzz`, `tables-c-wire-fuzz-negative-control`, `tables-c-message-negative-control` | ❌ #518 | ✅ `tables-go-wire-fuzz` `tables-go-wire-fuzz-negative-control` `tables-go-retain-wire-fuzz` (fixed and variable file/message forms) | ✅ `tables-cs-wire-fuzz` `tables-cs-region-fuzz` `tables-cs-retain-fuzz` `conformance-negative-control-cs` | ❌ #517 | ❌ #516 | ❌ #514 | ❌ #515 | C# carries the complete id-table file and message surface, native regions, mutable builders and retaining reads. Its storage choices and allocator @@ -917,7 +922,7 @@ read back as `0x7ff8000020000000`, which is the quiet bit the conversion set. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-float-nan` `tables-float-nan-negative-control`, and the TOOL's two engines (`TestOracleCarriesTheFloatBitPattern`, `TestOracleWidensTheFloatBitPattern`, `TestACookCarriesAFloatBitPattern`) | ❌ #366 | ❌ #366 | ✅ `internal/codegen/gotable/wire.go` (`tableWidenFloat`), `TestWireSignalingNaNWideningAndLEBOverflow`; hardware-conversion and tenth-byte-guard overlays both fail the regression | ✅ `tables-cs-leg` (integer-only float widening preserves NaN payload bits) | ❌ #366 | ❌ #366 | ❌ #366 | ❌ #366 | +| ✅ `tables-float-nan` `tables-float-nan-negative-control`, and the TOOL's two engines (`TestOracleCarriesTheFloatBitPattern`, `TestOracleWidensTheFloatBitPattern`, `TestACookCarriesAFloatBitPattern`) | ✅ `TestCTableWireWidening` `tables-c-wire-fuzz` | ❌ #366 | ✅ `internal/codegen/gotable/wire.go` (`tableWidenFloat`), `TestWireSignalingNaNWideningAndLEBOverflow`; hardware-conversion and tenth-byte-guard overlays both fail the regression | ✅ `tables-cs-leg` (integer-only float widening preserves NaN payload bits) | ❌ #366 | ❌ #366 | ❌ #366 | ❌ #366 | ### I1 — The independent allocation gate @@ -1339,7 +1344,7 @@ check removed reds on the report. | cpp | c | rust | go | cs | java | js | dart | elixir | |---|---|---|---|---|---|---|---|---| -| ✅ `tables-wire-fuzz` `tables-wire-fuzz-negative-control` | ❌ #492 | ❌ #492 | ✅ `tables-go-wire-fuzz` `tables-go-wire-fuzz-negative-control` `tables-go-builders` `tables-go-retain-wire-fuzz` | ✅ `tables-cs-wire-fuzz` `tables-cs-region-fuzz` `tables-cs-builder-fuzz` `tables-cs-retain-fuzz` `conformance-negative-control-cs` | ❌ #492 | ❌ #492 | ❌ #492 | ❌ #492 | +| ✅ `tables-wire-fuzz` `tables-wire-fuzz-negative-control` | ✅ `tables-c-wire-fuzz` `tables-c-wire-fuzz-negative-control` | ❌ #492 | ✅ `tables-go-wire-fuzz` `tables-go-wire-fuzz-negative-control` `tables-go-builders` `tables-go-retain-wire-fuzz` | ✅ `tables-cs-wire-fuzz` `tables-cs-region-fuzz` `tables-cs-builder-fuzz` `tables-cs-retain-fuzz` `conformance-negative-control-cs` | ❌ #492 | ❌ #492 | ❌ #492 | ❌ #492 | ### J1 — Accessor and descriptor agreement diff --git a/docs/SPEC-TABLES.md b/docs/SPEC-TABLES.md index 728b50ac4..dcaa2bda3 100644 --- a/docs/SPEC-TABLES.md +++ b/docs/SPEC-TABLES.md @@ -137,16 +137,15 @@ reach. going wide costs no allocation and no lock per node. **WHO CALLS THE ALLOCATOR IS PER FORM, and it is worth saying exactly.** The - arena's segments and `Lock`'s packed region are the RUNTIME's own calls — - `calloc` and `malloc` by name in both C++ and C — and a caller-provided - allocator is not threaded through them today. What the caller does own is the - REGION a load fills: `LoadMeasure` sizes it and the caller supplies it, which - is allocation with the caller holding the pointer. The BLOCK form (§19.1) is - the one surface that takes a caller-provided allocator with malloc semantics, - and there it is real: C++ takes an alloc/free pair with a context, C the same - three as a struct, used once at build time and never on the fill path. C# - threads a zeroed native allocation/free pair through arena, packing and - native numbering scratch; its managed authoring classes use the CLR. Go + C++ and C variable-length runtimes carry the caller's zeroed allocation/free + pair and context through arena segments, packing, numbering scratch and the + packed region. The default pair uses the runtime's allocation hooks (§6.5). + The REGION a load fills is caller-owned: `LoadMeasure` sizes it and the caller + supplies it. The BLOCK form (§19.1) takes a separate caller-provided allocator + with malloc semantics: C++ takes an alloc/free pair with a context, C the same + three as a struct, used once at build time and never on the fill path. + C# also threads a zeroed native allocation/free pair through arena, packing + and native numbering scratch; its managed authoring classes use the CLR. Go accepts allocation/free pairs for arenas, packing, numbering and block construction, with managed activation costs stated in PORTING.md. Other backends allocate inside their runtime and say so. No port contorts itself @@ -162,13 +161,37 @@ C-like dialect of `serialize.h`, with library calls behind hooks (§13.9). C++ and C carry both storage classes. C# carries both classes and the current id-table file and bitpacked message forms, JSON, runtime cook writing, block construction, native regions, builders, retain-unknown and UnitView. Go carries -these surfaces too, with its storage contracts described below. C, Dart, Rust, -Java, JavaScript and Elixir still carry their previously recorded wire surface -at this checkpoint; their current-form work is tracked separately. +these surfaces too. Dart, Rust, Java, JavaScript and Elixir still carry their previously recorded +wire surface at this checkpoint; their current-form work is tracked separately. Dart, Rust, Java, JavaScript and Elixir's fixed-class ports refuse pointers by name until their variable-class carry lands. Every generated language has a table backend; refusal is scoped to a construct, never the table declaration. +**WIRE FORM STATUS.** Section 3's id-table form is carried by the C++ +reference, the compiler engine (`internal/tablewire`), C, C# and Go. The C codec uses +full identities, canonical LEB128, first-use references, arm-kind framing, +flat node records and verdict-bearing reports. Graph JSON, pointer arrays, +byte/string blobs, wide scalars, fixed-point values, defaults and aliases ride +on that form. `tables-c-wire-fuzz` compares C with the independent engine. +Dynamic maps and lists use the same file form and graph JSON; +`tables-c-collections-fuzz` compares their recovery and native C region sizes +with C++ directly. C also writes canonical cooks in both byte orders, with +field-by-field padding normalization, and exposes named cook/block refusal +reasons and const block handles. Its UnitView registry includes table-free +units. C also reads and writes bitpacked message batches, with caller-owned +resolved announcement entries and native regions for graphs and collections. +C retains unknown fields in caller-owned storage for variable file roots and +message loads, and writes retained file roots (§6.6). Dart, Rust, Java, JavaScript and Elixir still write +the earlier form in this tree. [ROADMAP.md](../ROADMAP.md) records coverage by +construct and form. + +The C report has added `widened`, `retained`, `retain_lost`, `refused` and `reason` members after +`malformed`. Recompile callers with their generated headers and initialize a +fresh report with `{0}` or designated members; positional initializers must +account for the complete current structure. C refusal enum values are a local +generated API, not wire codes or a numeric ABI shared with C++. Compare the +named constants from the generated header rather than persisting their numbers. + **The Go backend carries fixed and variable tables.** Its file and announced message codecs use §3's id-table wire form. Caller-owned regions, mutable arena builders, bounded retention, runtime cook writers, block storage and UnitView @@ -1040,6 +1063,14 @@ enum is keyed. INDEX. Go's own bounds check still stands behind the array, so the failure mode is a wrong slot rather than a wrong page. + **C likewise stores a plain array.** Its generated per-field accessor is + `SCHEMA____AT(value, key)`. It supplies the declared + enum maximum itself, evaluates each argument once, and returns an lvalue; + const values remain const. For a pointer, pass `*pointer` as the value. + The generic `SCHEMA_TABLE_KEYED_AT(array, key, count)` is its implementation + helper; raw callers are responsible for supplying the correct count. + Direct C array subscripts likewise remain the caller's responsibility. + **ITERATION is still the surface a consumer of a whole array should reach for**, below, because it needs no key from the caller at all. @@ -1328,8 +1359,9 @@ is the buffer's own size argument one level up: a node pays for the sub-document it actually holds, and pays nothing to walk past one it never reads. -**Backend status: the C++ REFERENCE and the TOOL carry it; every other backend -refuses a unit that declares one, by name** (§11), and the ports are a named +**Backend status: the C++ REFERENCE and the TOOL carry it, and C carries its +file wire, region and text forms; the other backends refuse a unit that declares +one, by name** (§11), and the ports are a named follow-on (§15). The corpus holds the construct in `tables/blobs`: a small blob beside a caption, a blob past 64 KiB, a shared blob, and a present blob and string of length zero beside null slots, each crossing the wire and the @@ -3592,9 +3624,8 @@ moves not one byte of a value-only table: a fixed-size table has no pointer, therefore no node table, therefore exactly the bytes §3 already describes. -**Backend status for this section: the TOOL and the C++ REFERENCE write it; -the C port still writes the NESTED form, and every other backend refuses a -pointered unit's wire by name (§11).** +**Backend status for this section: the TOOL, C++ REFERENCE and C write it; +the other backends refuse a pointered unit's wire by name (§11).** `schema pack`'s engine — the compiler-side encoder and decoder this repo runs as tooling (§17.1) — and the generated C++ codecs are two implementations of this section, written from it rather than from each other, and each reads what @@ -3616,16 +3647,9 @@ unit's wire answers ABSENT for them, per case, so its FIXED-class pass is untouched and the gap is a number in the matrix rather than a paragraph somewhere. That is the row schema#349 fills, one language at a time. -**The C port is the one backend that carries a pointered unit and does not -carry this form yet**, because it was mirrored from the C++ backend while that -backend still wrote the earlier NESTED one: a pointee inline as a body under -kind `13`, no node table, and the three consequences that are the reasons this -form is the law — a node two parents name written once per parent, a pointer -chain that IS a nesting depth, and a cap on that depth. Its wire is part of -schema#349 with the seven that have no variable class at all. What every -backend DOES carry already is kind `17`'s row in the fixed-width skip rule, -because a fixed-class reader meeting a pointered writer's field has to step -over it whether or not it can write one. +The C port uses the same flat node table and preserves shared identity through +save, load, builder load and lock. Every fixed-class reader also carries kind +`17` in its skip rule, so it can pass a pointer field it cannot interpret. **What that edit MOVED, stated once.** A pointer field's kind changed from `13` to `17` and a pointered save gained the node table, so every pointered unit's @@ -7066,6 +7090,20 @@ edit and a name that is free today must not become a collision tomorrow. **The conformance rows below live on POINTERED units** for the same reason, and the fixed class's own row is the refusal. +**The C surface** is `root_load_retain`, `root_measure_retain` and +`root_save_retain`, with `root_load_retain_messages` taking a parallel +`TableRetain` array for the batch. `TableRetainId` is the C spelling of the +caller-owned ID entry, since C has no nested types. Fixed-root calls and +`root_save_retain_messages` are C99 macros that fail at the call site with a +named diagnostic. Ordinary bodies carry no retention parameter or branch; +the opt-in family reuses the field emitters with an explicit context passed +by value. Its private resolved records carry directory/path anchors and full +IDs. The resolving walk permits 64 framed levels and commits only complete +records that fit the caller's remaining capacity. A zero-bit message count +cannot bypass that output bound. Saving measures each retained frame once +before emitting its bytes. `TableReport.retained` and `retain_lost` carry the +load and save outcomes; `SaveRetain` requires a report. + **THE SURFACE: the caller's buffer, threaded through three calls.** ```cpp @@ -7943,9 +7981,12 @@ the wire, and keeps the flexibility that comes with it. BLOCK VALUE THAT SURFACE DOES NOT CARRY**, because it is a clause over the ADDRESS the caller passed and a manifest row hands a driver a path rather than a pointer. It is held in the C++ reference's own `BlockOpen` gate - instead, beside the four readings that are on the surface. Every other backend's `Open` - is still the null alone in its own spelling, and takes the parameter with - the wire form it lacks (§15). + instead, beside the four readings that are on the surface. C carries the + same clauses through `_open_ex` and `_block_open_ex`, with an + optional `TableRefuseReason *` last parameter; the existing entry points + forward with a null reason pointer. The C reason type preserves the `int` + out-parameter ABI and names the values `SCHEMA_TABLE_REFUSE_*`, in the order + above. Other backends take the parameter with the wire form they lack (§15). - **`Open` is the RUNTIME's only entry point.** There is no second one: a build either wrote a file or it did not, and the build version is what says @@ -8818,6 +8859,23 @@ int64_t SettingsCookMeasure( const Settings & value ); bool SettingsCook( const Settings & value, void * out, uint64_t capacity, TableByteOrder order ); ``` +**THE C SURFACE** uses pointers and explicit contexts, retaining the same +measure/write contract: + +```c +int64_t settings_cook_measure(const Settings * value); +int settings_cook(const Settings * value, void * out, uint64_t capacity, TableByteOrder order); +int64_t scene_cook_measure(const TableCtx * ctx, const Scene * value); +int scene_cook(const TableCtx * ctx, const Scene * value, void * out, uint64_t capacity, TableByteOrder order); +``` + +A region or opened cook uses a null context; an editable builder uses a context +whose `arena` points at the builder's arena. `TableByteOrder_Little` and +`TableByteOrder_Big` select the target order. Variable roots also expose +`_cook_with_allocator` and `_cook_measure_with_allocator`, with a final +`TableAllocator` argument. The default pair is the builder's when a context +names one, and the `schema_allocate`/`schema_release` hooks otherwise. + - **IT IS A MEASURE/WRITE SPLIT, exactly as the wire's is** (§6.1). `CookMeasure` answers the whole file's length — the header, the data part and the attribution part — in `int64_t`, because a cook's part lengths are 64 @@ -9433,12 +9491,13 @@ function per unit, name-first in the unit's own namespace beside `ProtocolId` (SPEC §6.1), answering with the set of everything the build declared: -**PORT STATUS: the C++ reference emits it; the eight ports do not yet.** The -C++ backend emits the pair for every unit that declares a table, with the -corpus gate of §8.7 holding its listing to the compiler's own. A TABLE-FREE -unit's view waits on the type view in the packet backend, which is where the -descriptors of a unit with no table closure would have to come from; that and -the eight ports are named follow-ons (§15). +**PORT STATUS: C++ and C emit the registry.** C++ emits its pair for units +that declare a table. C emits `View.h` and `View.c` for +every unit, including a table-free unit, with `unit_view()` as its entry point. +The independently generated listing in §8.7 checks both. The C registry and +its descriptors are immutable static data, with program lifetime; they own no +decoded region. A caller using a descriptor to inspect a value must still keep +that value's storage alive. Other ports' registries remain follow-ons (§15). ```cpp struct ViewConstant @@ -9633,7 +9692,8 @@ here?" — §2.2's own distinction between machinery and columns. ### 8.5 On the side **The view is one generated file per UNIT, not per schema file** — -`View.h` and `View.cpp` in C++, `View.cs` in C#. +`View.h` and `View.cpp` in C++, `View.h` and +`View.c` in C, `View.cs` in C#. **The name is `capitalize(package)` followed by `View`** in every target that emits one: `package tabledemo` gives `TabledemoView.h`. It is a FILE name, and generated file names are basename-shaped in every target, so it @@ -10092,7 +10152,8 @@ in build version (§20.5). whose lookup surface is members spells the same eight on the storage type and claims nothing at file scope for them. And the generated ENTRY is a closure member like every other, so it claims the suffix set below in its - own right. + own right. C additionally claims `
FindMut` for lookup in a + mutable arena. - **Unbounded arrays** (§2.9): a `[]T` in a `type` body, which is what keeps the type wire's "no unbounded collections" true (SPEC.md §1) and what refuses one on a packet; **a `[]T` or `[]*T` as a UNION ARM** (§2.6), the @@ -10110,21 +10171,22 @@ in build version (§20.5). bounded array's own diagnostic and naming its own follow-on, which is `[][]T`, `[]map[K]V`, `[]?T` and `[]*bytes`; an element whose ALIGNMENT exceeds the arena's, naming the field and the alignment it asks for (§2.9); - and a declaration under any of the three names the construct CLAIMS. + and a declaration under any of the four names the construct CLAIMS. - **AN UNBOUNDED ARRAY CLAIMS THREE NAMES AGAINST ITS FIELD**, on the map's own + **AN UNBOUNDED ARRAY CLAIMS FOUR NAMES AGAINST ITS FIELD**, on the map's own rule: `
` followed by the PascalCase of the field's name, and then - `Add`, `Each` or `Erase`. A `Save` with a `placements` list therefore claims - `SavePlacementsAdd`, `SavePlacementsEach` and `SavePlacementsErase`, and a - declaration spelling any of the three is refused naming the field. **It - claims THREE where a map claims eight**, and the difference is the key on + `Add`, `Each`, `Erase` or `At`. A `Save` with a `placements` list therefore claims + `SavePlacementsAdd`, `SavePlacementsEach`, `SavePlacementsErase` and + `SavePlacementsAt`. A declaration spelling any of the four is refused naming + the field. `At` is the C indexed accessor over a region list. **It + claims FOUR where a map claims eight**, and the difference is the key on both sides: `Add` is the one name a list has that a map does not, because an append needs no key where an insert does, and of the map's eight it does not claim `Entry`, because no entry table is generated (§2.9), `Insert` and `Find`, because there is no key to insert under or look up by, or `IndexMeasure`, `Index` and `IndexFind`, because there is no lookup to accelerate. `Erase` it does claim, and it is addressed by the element's own - pointer (§2.9). A language whose surface is members spells the same three on + pointer (§2.9). A language whose surface is members spells these operations on the storage type and claims nothing at file scope for them. - **Byte buffers** (§2.5): `*bytes` or `*string` outside a table body; a bound on one, `*bytes(N)` (a buffer at its used size has no bound to @@ -10173,9 +10235,9 @@ in build version (§20.5). makes it one. **A payload-free arm is outside the class**: it has no payload, so it rides the packet wire as its tag alone and a `type` body takes it in all nine backends (SPEC §4.8). **A payload-free arm reached by - a table closure is C++ only**, and the other eight targets refuse it + a table closure is carried by C and C++**, and the other seven targets refuse it naming the union and the target. And **a TABLE-CLOSURE union under every - backend but C++** is refused naming the union and the target: the ports are + backend but C and C++** is refused naming the union and the target: the ports are a named follow-on (§15), and a port that emitted the union would name a table it never declares, or overlay storage its fixed-class codecs never met. - **On an ARM** (§2.6, which states each reason): a specified default; `?`; @@ -10342,7 +10404,7 @@ in build version (§20.5). Counts Row BlockProjection ``` - **THE C BACKEND CLAIMS SEVEN MORE**, and the checker claims them on the same + **THE C BACKEND CLAIMS THESE ADDITIONAL SPELLINGS**, and the checker claims them on the same terms. C++ and C# put these on a class — a builder's `Lock`, a storage's `Create`, a block type's `Type` — and a member function claims nothing; C has no members, so each is a free function under its owner's name (§6.1's C @@ -10350,7 +10412,16 @@ in build version (§20.5). ``` BuilderInit BuilderShutdown BuilderLock BuilderRoot + BuilderInitWithAllocator + MeasureRetainWithAllocator SaveRetainWithAllocator + LoadMeasureMessages LoadMeasureMessagesEx + MeasureMessagesWithAllocator SaveMessagesWithAllocator + LoadMeasureEx MeasureWithAllocator SaveWithAllocator + ToJsonWithAllocator ToJsonMeasureWithAllocator BlockStorageCreate BlockStorageDestroy BlockType + CookWithAllocator CookMeasureWithAllocator CookExtent OpenEx + BlockOpenEx BlockOpenConst BlockOpenConstEx BlockOpenCheck + BlockConst BlockBytesConst ``` **`Row` and `BlockProjection` are the C# BLITTABLE records' names** (§19.2), @@ -10359,7 +10430,7 @@ in build version (§20.5). noun collides with declarations in OTHER units of the same assembly, which a compiler that sees one unit cannot refuse. - **A table also claims TWO PER OUT-OF-LINE ARRAY**, because its row + **A table also claims names PER OUT-OF-LINE ARRAY**, because its row accessors are named after its fields: `
` followed by the PascalCase of the field's name hands back that field's rows, and the same name with `Span` appended is the contiguous view (§19.2) — so `RenderFrame` with a @@ -10367,7 +10438,10 @@ in build version (§20.5). declaration spelling either is refused naming both. That part of the set moves with the declaration, which is why it is a rule here rather than a list. A language whose accessors are members spells the same two names on - the block type and claims nothing at file scope for them. + the block type and claims nothing at file scope for them. C also claims + `
Rows`, `
RowsConst` and + `
SpanConst`, for its mutable row handle and its const row/span + readers. The emitted functions use snake_case, as the other C entry points do. **THE DESCRIPTOR SURFACE'S CLAIMS ARE UNCONDITIONAL — every declaration, every unit, tables or not.** Every unit is to emit a view file and that file @@ -11447,15 +11521,16 @@ inspects everything in the schema built: reachable. Closing it is the same one-line predicate the pair rule is already spelled as, plus a corpus unit per spelling; the page's rule is already the one above and nothing about it is undecided. -- **THE UNIT REGISTRY IN THE EIGHT PORTS** (§8.3, §8.7). The C++ reference - emits `UnitView()` and the eight ports do not, so in those eight an enum +- **THE UNIT REGISTRY IN THE SEVEN REMAINING PORTS** (§8.3, §8.7). C++ and C + emit the registry; the remaining seven ports do not in this tree, so an enum VARIANT's, a flags BIT's and a record-naming ARM's `doc` and `tags` reach the IR and the generated comments and no descriptor column. What a port lands is `ViewConstant`, `ViewVariant`, `ViewVocabulary` and `ViewType` with their three annotation members, and its own program byte-compared against the same pin — the corpus listing gate and the two same-declaration pairs are written once, against the IR, and every backend answers to them. -- **THE VIEW OF A TABLE-FREE UNIT** (§8.2, §8.5). The C++ view is emitted by +- **THE VIEW OF A TABLE-FREE UNIT IN C++** (§8.2, §8.5). C emits its unit + registry for table-free units. The C++ view is emitted by the TABLE backend, so a unit that declares no table gets none — and a packet-only unit's `type` declarations therefore carry no descriptor at all today. Landing it is the type view in the packet backend: the descriptor @@ -11479,8 +11554,8 @@ inspects everything in the schema built: a catalog is re-cooked when any table in its unit moves (§7). Everything about it is a decision — who declares the set, what proves two versions interchangeable — and none of that is decided here. -- **THE COOK's WRITE SIDE in every other language.** C++ writes a cook of - either class today and its bytes are the tool's (§7.6). Every other +- **THE COOK's WRITE SIDE in every other language.** C++ and C write cooks of + either class today and their bytes are the tool's (§7.6). Every other language's writer is the same feature in that language's own idiom, held to the same `cook-write` surface — a language writes, the harness compares to the tool's bytes — and a pointered root's needs what the C++ one needed: the @@ -11701,7 +11776,7 @@ inspects everything in the schema built: belongs with the emitters rather than with this page, because a snapshot taken before the code that could move it is a snapshot of nothing. - **The view in a ported backend** — C++ carries it (§8); every other backend, - C# included, emits no view file until it emits the same registry against the + except C, emits no view file until it emits the same registry against the same pin (§8.7). Nothing is refused meanwhile, because nothing in a schema asks for one (§8.4): a backend without the emitter is a backend whose users have no registry, and the status paragraph says so. @@ -11718,12 +11793,13 @@ inspects everything in the schema built: the construct — the parser's `map[K]V`, the checker's refusals, the generated entry table with its record and its two constant ids — and every backend refuses a unit that declares one, by name (§11), until its codec lands. The - C++ reference is first and carries the whole construct: the builder surface + C++ reference carries the whole construct: the builder surface (insert, erase, find, iterate), the sort in the four walks, the region load's ascending check with its `duplicate` and `malformed` events, the const `Find`, the text form's object and `schema cook-check`'s map-slot clause with its order check - (§7.4). The tool's COOK and UNCOOK halves are the one piece still owed for + (§7.4). C carries the file wire, region and text forms, including the optional + runtime index. The tool's COOK and UNCOOK halves are the one piece still owed for this construct: a map-bearing unit is refused by name at those two surfaces, because a map adds the sort, the entry array's key order and the two reader events to the node extent the list's own halves already @@ -11740,9 +11816,10 @@ inspects everything in the schema built: construct, which is the parser's `[]T`, the checker's refusals and its three claimed names, and the record's reference-and-count slot, and every port refuses a unit that declares one, by name (§11), until its codec lands. The - C++ reference carries it: the builder's segments and `Add`, the four walks - in index order, the region load, the const `TableList` surface, the text - form's array, and `schema cook-check`'s element-array clause in the tool. + C++ reference carries it; C carries the file wire, region and text forms, + including the builder's segments and `Add`, the walks in index order, the + region load, the const `TableList` surface and the text form's array. The tool + also carries `schema cook-check`'s element-array clause. The TOOL carries the construct whole: its cook lays the element arrays in the holder's node extent in the same PRE-ORDER the reference lays them and lands on the reference's bytes exactly, in both byte orders, and its uncook @@ -11761,7 +11838,7 @@ inspects everything in the schema built: and not about the bound. - Keyed lookup conveniences over loaded collections (library-side, never stored semantics). -- **AN ARRAY OF UNIONS in every ported backend** (§2.6): C++ and the tool +- **AN ARRAY OF UNIONS in the remaining ported backends** (§2.6): C, C++ and the tool carry `[..N]Body` and `[N]Body`, and every other backend refuses a table closure holding one, by name (§11). What a port needs is the union element in its fixed-class walks — measure, save, load, the descriptors' arms column @@ -12000,15 +12077,12 @@ SceneFromJson( builder, text, text_bytes, &report ); ``` **Backend status for this section: the FIXED class in all nine — C++, C, C#, -Dart, Elixir, Go, Java, JavaScript and Rust — and the VARIABLE class in C++ -(§16.7).** A pointered -unit's text form is the C++ reference's, through the builder, and carrying it -to the other backends is schema#349's row beside the wire. In C#, Dart, +Dart, Elixir, Go, Java, JavaScript and Rust — and the VARIABLE class in C and +C++ (§16.7).** A pointered unit's text form reads through the builder. In C#, Dart, Elixir, Go, Java, JavaScript and Rust the absence is already made one level up: a pointered unit gets no table source at all (§11), so it has no text form for -the same reason it has no wire codec; the C port has the wire's earlier form -(§3.1) and its text form follows it. +the same reason it has no wire codec. **The FLOAT SPELLING is C's `%.*g`, byte for byte, in every port**, and each says how it gets there rather than reaching for its runtime's default. C++ @@ -13816,7 +13890,10 @@ schema name, as everywhere else in that backend. they were, and a producer holds a mutable block exactly as before. The split is the one C# states above with `ref readonly` and `ReadOnlySpan`, and the one Rust has by returning `&[Row]`. **A write through a const view is a - COMPILE ERROR**, held by a negative compile control (§19.5). + COMPILE ERROR**, held by a negative compile control (§19.5). C spells its + reader `BlockConst` with `_block_open_const` and the `_ex` variant + for a reason, and provides `_rows_const` and `_span_const` accessors. Both + handles use one bytewise gate; the reader's row pointers remain const. - **An array is ITERATED, not indexed by hand.** The accessor yields a reference to each row where it lies, at the pitch the instance gives, for `count` rows — a range-for in C++, an enumerator in C#, the equivalent per diff --git a/docs/SPEC.md b/docs/SPEC.md index cac628518..580d65fab 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -731,7 +731,7 @@ FloatExpr = float expression over float literals, int literals and const names On the TABLE wire a field at its declared default elides and an absent field reads as it, whatever the kind (SPEC-TABLES.md §4), so the string, bytes and flags defaults are part of that wire's contract exactly - as a scalar's is. C++ carries all three on both wires. The other eight + as a scalar's is. C and C++ carry all three on both wires. The other seven built-in backends carry them for packet-only fields and refuse a default reachable from any table by name. **On the packet wire, defaults initialize storage without eliding fields.** diff --git a/docs/USAGE.md b/docs/USAGE.md index 6b7bf6044..dce6cff77 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -1745,6 +1745,22 @@ graphdemo::SceneLoadMessages( roots, &root_count, region.data(), region_bytes, vocabulary, wire, wire_bytes, &report ); ``` +In C the same operations use `announce_measure`, `announce`, `announce_read`, +`_measure_messages`, `_save_messages` and `_load_messages`. +Create a vocabulary with `table_vocabulary(entries, capacity)`, where `entries` +is an array of `TableMessageEntry`. Those resolved entries belong to the caller +and must outlive the vocabulary; the announcement buffer can be released after +`announce_read`. Keep one vocabulary per connection direction. Its default byte +limit is 65,536; `max_bytes` and the entry capacity are independent caller limits. + +Fixed roots take an array of values. Variable roots take an array of pointers to +loaded or locked roots; reads use one caller-owned region sized by +`_load_measure_messages(vocabulary, wire, bytes)`. The `_ex` variant also +returns attribution bytes and a refusal reason. The variable message writers +provide `_with_allocator` variants for their temporary graph numbering. Message +reads allocate nothing. A damaged body can return a partially decoded root, but +the output count includes only complete bodies. + **What you get.** The three backend messages measured on schema#523 go from 106, 273 and 104 bytes to 52, 148 and 43, and from 10, 43 and 10 to 3, 11 and 3 when every field sits at its declared default. Sent as ONE batch under an diff --git a/generated/bench/c/BenchView.c b/generated/bench/c/BenchView.c new file mode 100644 index 000000000..1f9367fdd --- /dev/null +++ b/generated/bench/c/BenchView.c @@ -0,0 +1,239 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "BenchView.h" +static const TableFieldInfo schema_bench_bench_bits_fields_[] = { + { "b7", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( BenchBits, b7 ), (uint32_t) sizeof( ( (BenchBits *) 0 )->b7 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b13", NULL, "bits(13)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( BenchBits, b13 ), (uint32_t) sizeof( ( (BenchBits *) 0 )->b13 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b23", NULL, "bits(23)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( BenchBits, b23 ), (uint32_t) sizeof( ( (BenchBits *) 0 )->b23 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8.388607e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b3", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( BenchBits, b3 ), (uint32_t) sizeof( ( (BenchBits *) 0 )->b3 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b32", NULL, "bits(32)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( BenchBits, b32 ), (uint32_t) sizeof( ( (BenchBits *) 0 )->b32 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b11", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( BenchBits, b11 ), (uint32_t) sizeof( ( (BenchBits *) 0 )->b11 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b19", NULL, "bits(19)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( BenchBits, b19 ), (uint32_t) sizeof( ( (BenchBits *) 0 )->b19 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 524287.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b48", NULL, "bits(48)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( BenchBits, b48 ), (uint32_t) sizeof( ( (BenchBits *) 0 )->b48 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_bench_bits_info_ = { "BenchBits", (uint32_t) sizeof( BenchBits ), 8, schema_bench_bench_bits_fields_, schema_bench_bench_bits_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_bench_bench_ints_fields_[] = { + { "f0", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f0 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f0 ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f1", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f1 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f1 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f2", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f2 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f2 ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+06, 1e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f3", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f3 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f3 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f4", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f4 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f4 ), 0xffffffffu, 0xffffffffu, NULL, 1, -15.0, 15.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f5", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f5 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f5 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f6", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f6 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f6 ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f7", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f7 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f7 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f8", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f8 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f8 ), 0xffffffffu, 0xffffffffu, NULL, 1, -600000.0, 600000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f9", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchInts, f9 ), (uint32_t) sizeof( ( (BenchInts *) 0 )->f9 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_bench_ints_info_ = { "BenchInts", (uint32_t) sizeof( BenchInts ), 10, schema_bench_bench_ints_fields_, schema_bench_bench_ints_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_bench_bench_mixed_server_time_outside_range_ = { { 0ull, 0ull }, { 16776960ull, 0ull } }; +static const TableVariantInfo schema_bench_bench_mixed_game_event_outside_variants_[] = { + { "None", 0 }, + { "hit", 0x0000000000000000ull }, + { "chat", 0x0000000000000000ull }, + { "pickup", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_bench_bench_mixed_game_event_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( MixedEvent, as.hit ), &schema_bench_mixed_hit_event_info_, NULL, (uint32_t)sizeof( ((MixedEvent *)0)->as.hit ) }, + { (uint32_t)offsetof( MixedEvent, as.chat ), &schema_bench_mixed_chat_event_info_, NULL, (uint32_t)sizeof( ((MixedEvent *)0)->as.chat ) }, + { (uint32_t)offsetof( MixedEvent, as.pickup ), &schema_bench_mixed_pickup_event_info_, NULL, (uint32_t)sizeof( ((MixedEvent *)0)->as.pickup ) }, +}; +static const TableUnionInfo schema_bench_bench_mixed_game_event_outside_union_ = { (uint32_t) offsetof( MixedEvent, type ), (uint32_t) sizeof( ( (MixedEvent *) 0 )->type ), schema_bench_bench_mixed_game_event_outside_arms_ }; +static const TableWideRange schema_bench_bench_mixed_wide_key_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableWideRange schema_bench_bench_mixed_flux_outside_range_ = { { 0ull, 18446744004990074880ull }, { 0ull, 68719476736ull } }; +static const TableWideRange schema_bench_bench_mixed_ping_outside_range_ = { { 0ull, 0ull }, { 64000ull, 0ull } }; +static const TableFieldInfo schema_bench_bench_mixed_fields_[] = { + { "sequence", NULL, "bits(16)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, sequence ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->sequence ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ack_sequence", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, ack_sequence ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->ack_sequence ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ack_bits", NULL, "bits(32)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, ack_bits ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->ack_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "session_id", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, session_id ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->session_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "client_id", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, client_id ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->client_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "nonce", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, nonce ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->nonce ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "world_time", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, world_time ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->world_time ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+12, 1e+12, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "frame_tick", NULL, "bits(48)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, frame_tick ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->frame_tick ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "server_time", NULL, "fixed(24, 8)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, server_time ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->server_time ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, &schema_bench_bench_mixed_server_time_outside_range_, 8, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "entities", NULL, "MixedEntity", 0x0000000000000000ull, 13, 1, 1, 0, 8, (uint32_t) offsetof( BenchMixed, entities ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->entities[0] ), (uint32_t) offsetof( BenchMixed, entities_count ), 0xffffffffu, &schema_bench_mixed_entity_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "stats", NULL, "MixedStat", 0x0000000000000000ull, 13, 1, 1, 0, 80, (uint32_t) offsetof( BenchMixed, stats ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->stats[0] ), (uint32_t) offsetof( BenchMixed, stats_count ), 0xffffffffu, &schema_bench_mixed_stat_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "game_event", NULL, "MixedEvent", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, game_event ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->game_event ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_bench_bench_mixed_game_event_outside_variants_, 1, NULL, NULL, -1, &schema_bench_bench_mixed_game_event_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "loadout", NULL, "uint8", 0x0000000000000000ull, 6, 1, 0, 0, 4, (uint32_t) offsetof( BenchMixed, loadout ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->loadout[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "player_name", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 15, (uint32_t) offsetof( BenchMixed, player_name ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->player_name ), (uint32_t) offsetof( BenchMixed, player_name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "payload", NULL, "bytes", 0x0000000000000000ull, 6, 1, 1, 0, 16, (uint32_t) offsetof( BenchMixed, payload ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->payload[0] ), (uint32_t) offsetof( BenchMixed, payload_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "aim_x", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, aim_x ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->aim_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "aim_y", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, aim_y ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->aim_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "aim_z", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, aim_z ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->aim_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "recoil", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, recoil ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->recoil ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "drift", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, drift ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->drift ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "wide_key", NULL, "uint128", 0x0000000000000000ull, 19, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, wide_key ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->wide_key ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_bench_bench_mixed_wide_key_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flux", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, flux ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->flux ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.2676506002282294e+30, 1.2676506002282294e+30, &schema_bench_bench_mixed_flux_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ping", NULL, "ufixed(8, 8)", 0x0000000000000000ull, 26, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, ping ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->ping ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 250.0, &schema_bench_bench_mixed_ping_outside_range_, 8, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "crc_hint", NULL, "bits(24)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, crc_hint ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->crc_hint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.6777215e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_extra", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, has_extra ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->has_extra ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "extra", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, extra ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->extra ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "has_extra", TableDocNone, 0, NULL, 0, NULL }, + { "idle_ticks", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchMixed, idle_ticks ), (uint32_t) sizeof( ( (BenchMixed *) 0 )->idle_ticks ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 15.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!has_extra", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_bench_mixed_info_ = { "BenchMixed", (uint32_t) sizeof( BenchMixed ), 27, schema_bench_bench_mixed_fields_, schema_bench_bench_mixed_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_bench_bench_packet_fields_[] = { + { "a", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, a ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, b ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "c", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, c ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->c ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+06, 1e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "bits7", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, bits7 ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->bits7 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "bits13", NULL, "bits(13)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, bits13 ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->bits13 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "bits23", NULL, "bits(23)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, bits23 ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->bits23 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8.388607e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, flag ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "x", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, x ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, y ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, z ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "big", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( BenchPacket, big ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->big ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "blob", NULL, "uint8", 0x0000000000000000ull, 6, 1, 0, 0, 17, (uint32_t) offsetof( BenchPacket, blob ), (uint32_t) sizeof( ( (BenchPacket *) 0 )->blob[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_bench_packet_info_ = { "BenchPacket", (uint32_t) sizeof( BenchPacket ), 12, schema_bench_bench_packet_fields_, schema_bench_bench_packet_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_bench_mixed_chat_event_fields_[] = { + { "channel", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedChatEvent, channel ), (uint32_t) sizeof( ( (MixedChatEvent *) 0 )->channel ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "speaker", NULL, "bits(12)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( MixedChatEvent, speaker ), (uint32_t) sizeof( ( (MixedChatEvent *) 0 )->speaker ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_mixed_chat_event_info_ = { "MixedChatEvent", (uint32_t) sizeof( MixedChatEvent ), 2, schema_bench_mixed_chat_event_fields_, schema_bench_mixed_chat_event_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_bench_mixed_entity_weapon_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Fists", 0x0000000000000000ull }, + { "Pistol", 0x0000000000000000ull }, + { "Shotgun", 0x0000000000000000ull }, + { "Rifle", 0x0000000000000000ull }, + { "Sniper", 0x0000000000000000ull }, + { "Smg", 0x0000000000000000ull }, + { "Rocket", 0x0000000000000000ull }, + { "Grenade", 0x0000000000000000ull }, + { "Plasma", 0x0000000000000000ull }, + { "Railgun", 0x0000000000000000ull }, + { "Flamer", 0x0000000000000000ull }, + { "Mine", 0x0000000000000000ull }, + { "Turret", 0x0000000000000000ull }, + { "Drone", 0x0000000000000000ull }, + { "Repair", 0x0000000000000000ull }, +}; +static const TableVariantInfo schema_bench_mixed_entity_damage_outside_variants_[] = { + { "Bleeding", 0 }, + { "Burning", 0 }, + { "Stunned", 0 }, + { "Slowed", 0 }, + { "Poisoned", 0 }, + { "Shielded", 0 }, + { "Airborne", 0 }, + { "Downed", 0 }, +}; +static const TableFieldInfo schema_bench_mixed_entity_fields_[] = { + { "entity_id", NULL, "bits(12)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, entity_id ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->entity_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pos_x", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, pos_x ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->pos_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pos_y", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, pos_y ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->pos_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pos_z", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, pos_z ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->pos_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "yaw", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, yaw ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->yaw ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pitch", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, pitch ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->pitch ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "vel_x", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, vel_x ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->vel_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "vel_y", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, vel_y ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->vel_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "vel_z", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, vel_z ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->vel_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "health", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, health ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "weapon", NULL, "MixedWeapon", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, weapon ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->weapon ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 15, schema_bench_mixed_entity_weapon_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "damage", NULL, "MixedDamage", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, damage ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 7, schema_bench_mixed_entity_damage_outside_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "moving", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, moving ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->moving ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "firing", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( MixedEntity, firing ), (uint32_t) sizeof( ( (MixedEntity *) 0 )->firing ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_mixed_entity_info_ = { "MixedEntity", (uint32_t) sizeof( MixedEntity ), 14, schema_bench_mixed_entity_fields_, schema_bench_mixed_entity_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_bench_mixed_hit_event_fields_[] = { + { "target_id", NULL, "bits(12)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( MixedHitEvent, target_id ), (uint32_t) sizeof( ( (MixedHitEvent *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "damage", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedHitEvent, damage ), (uint32_t) sizeof( ( (MixedHitEvent *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "hit_kind", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedHitEvent, hit_kind ), (uint32_t) sizeof( ( (MixedHitEvent *) 0 )->hit_kind ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "crit", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( MixedHitEvent, crit ), (uint32_t) sizeof( ( (MixedHitEvent *) 0 )->crit ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_mixed_hit_event_info_ = { "MixedHitEvent", (uint32_t) sizeof( MixedHitEvent ), 4, schema_bench_mixed_hit_event_fields_, schema_bench_mixed_hit_event_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_bench_mixed_pickup_event_fields_[] = { + { "item_id", NULL, "bits(10)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( MixedPickupEvent, item_id ), (uint32_t) sizeof( ( (MixedPickupEvent *) 0 )->item_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1023.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "amount", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedPickupEvent, amount ), (uint32_t) sizeof( ( (MixedPickupEvent *) 0 )->amount ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_mixed_pickup_event_info_ = { "MixedPickupEvent", (uint32_t) sizeof( MixedPickupEvent ), 2, schema_bench_mixed_pickup_event_fields_, schema_bench_mixed_pickup_event_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_bench_mixed_stat_fields_[] = { + { "stat_id", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( MixedStat, stat_id ), (uint32_t) sizeof( ( (MixedStat *) 0 )->stat_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "delta", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( MixedStat, delta ), (uint32_t) sizeof( ( (MixedStat *) 0 )->delta ), 0xffffffffu, 0xffffffffu, NULL, 1, -512.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_bench_mixed_stat_info_ = { "MixedStat", (uint32_t) sizeof( MixedStat ), 2, schema_bench_mixed_stat_fields_, schema_bench_mixed_stat_reset_raw_, TableDocNone, 0, NULL }; + +const UnitViewInfo * schema_bench_unit_view_(void) +{ + static const ViewVariant MixedWeapon_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Fists", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Pistol", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Shotgun", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Rifle", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "Sniper", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 6, "Smg", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 7, "Rocket", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 8, "Grenade", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 9, "Plasma", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 10, "Railgun", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 11, "Flamer", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 12, "Mine", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 13, "Turret", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 14, "Drone", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 15, "Repair", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant MixedDamage_view_variants[] = { + { 0, "Bleeding", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Burning", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Stunned", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Slowed", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Poisoned", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "Shielded", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 6, "Airborne", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 7, "Downed", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant MixedEvent_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "hit", 0x0000000000000000ull, "MixedHitEvent", &schema_bench_mixed_hit_event_info_, NULL, TableDocNone, 0, NULL }, + { 2, "chat", 0x0000000000000000ull, "MixedChatEvent", &schema_bench_mixed_chat_event_info_, NULL, TableDocNone, 0, NULL }, + { 3, "pickup", 0x0000000000000000ull, "MixedPickupEvent", &schema_bench_mixed_pickup_event_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "MixedWeapon", "Bench.schema", 15, 8, 16, MixedWeapon_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary flags_decls[] = { + { "MixedDamage", "Bench.schema", 7, 8, 8, MixedDamage_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary unions[] = { + { "MixedEvent", "Bench.schema", 3, 8, 4, MixedEvent_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "BenchBits", "Bench.schema", 0, &schema_bench_bench_bits_info_, TableDocNone, 0, NULL }, + { "BenchInts", "Bench.schema", 0, &schema_bench_bench_ints_info_, TableDocNone, 0, NULL }, + { "BenchMixed", "Bench.schema", 0, &schema_bench_bench_mixed_info_, TableDocNone, 0, NULL }, + { "BenchPacket", "Bench.schema", 0, &schema_bench_bench_packet_info_, TableDocNone, 0, NULL }, + { "MixedChatEvent", "Bench.schema", 0, &schema_bench_mixed_chat_event_info_, TableDocNone, 0, NULL }, + { "MixedEntity", "Bench.schema", 0, &schema_bench_mixed_entity_info_, TableDocNone, 0, NULL }, + { "MixedHitEvent", "Bench.schema", 0, &schema_bench_mixed_hit_event_info_, TableDocNone, 0, NULL }, + { "MixedPickupEvent", "Bench.schema", 0, &schema_bench_mixed_pickup_event_info_, TableDocNone, 0, NULL }, + { "MixedStat", "Bench.schema", 0, &schema_bench_mixed_stat_info_, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "bench", 0x8d12c3149393f40full, + 9, types, + 0, NULL, + 1, enums, + 1, flags_decls, + 1, unions, + 0, NULL }; + return &info; +} diff --git a/generated/bench/c/BenchView.h b/generated/bench/c/BenchView.h new file mode 100644 index 000000000..7657611df --- /dev/null +++ b/generated/bench/c/BenchView.h @@ -0,0 +1,503 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_BENCH_VIEW_H +#define SCHEMA_BENCH_VIEW_H +#include +#include +#include +#include "Bench.h" +#ifndef SCHEMA_BENCH_TABLE_PRIMITIVES +#define SCHEMA_BENCH_TABLE_PRIMITIVES + +/* THE CODEC DOES NOT DEPEND ON THE COMPILER'S INLINING BUDGET. A table of a + realistic field count emits one large body per type, and the cursor a body + writes through lives in the caller's TableWriter: across a call boundary that + cursor round-trips through memory, and a uint8_t * store may alias the writer + itself, so every put reloads it. When a budget runs out mid-body the codec + silently degrades to that shape. Forcing the primitives and the fixed-class + bodies inline is what keeps the cursor in registers and lets adjacent + constant framing bytes merge into one store. + + The spelling is FEATURE TESTED rather than assumed, exactly as the packet + emitter's own inlining demand is (SPEC §6.1's C column). + + IT CARRIES THE PACKAGE for the reason every other name here does, and that + reason is the LINKER rather than the preprocessor: two units of the same + schema family link into one program — two generations of one schema is the + case the conformance driver itself is — so nothing this backend emits may + be spelled the same way by two packages. They do NOT share a translation + unit and cannot: C has no namespace, so two units' headers in one TU + redefine TableReport, TableWriter and every other runtime name, which is the + packet emitter's standing limit (SPEC §6.1) and not something tables + changed. A per-package macro is what keeps the LINK legal. */ +#ifndef SCHEMA_BENCH_TABLE_INLINE +#if defined( _MSC_VER ) +#define SCHEMA_BENCH_TABLE_INLINE __forceinline +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_BENCH_TABLE_INLINE inline __attribute__(( always_inline )) +#else +#define SCHEMA_BENCH_TABLE_INLINE inline +#endif +#endif + +/* A COMPILE-TIME assertion under -std=c99, which has none of its own. C11's + _Static_assert carries the message into the build log verbatim and is used + wherever the compiler has it; everywhere else the negative array bound is + the same refusal with a tag a reader can find. The tag is unique per record + and field, and each record is asserted by the ONE header that declares it, + so no two of these ever meet. */ +#ifndef SCHEMA_TABLE_STATIC_ASSERT +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) _Static_assert( cond, message ) +#elif defined( __cplusplus ) && __cplusplus >= 201103L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) static_assert( cond, message ) +#else +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) typedef char schema_table_assert_##tag[ ( cond ) ? 1 : -1 ] +#endif +#endif + +/* A record's ALIGNMENT, which the cook's layout contract asserts and which the + cook's Open compares the header's alignment word against (§7.1, §20.3). + C99 has no alignof either; C11's _Alignof, the two GNU spellings and MSVC's + cover every compiler this repo builds under, and the last form is the + portable definition of the same number. */ +#ifndef SCHEMA_TABLE_ALIGNOF +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_ALIGNOF( T ) _Alignof( T ) +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof__( T ) +#elif defined( _MSC_VER ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof( T ) +#else +#define SCHEMA_TABLE_ALIGNOF( T ) ( (size_t) offsetof( struct { char schema_pad_; T schema_member_; }, schema_member_ ) ) +#endif +#endif + +/* The table-wire read report — the permissive contract's ledger. Silence + (all zero) means the data matched this reader's schema exactly. */ +typedef struct TableReport +{ + int32_t unknown; /* unknown field ids skipped (newer data) */ + int32_t kind_mismatch; /* known id, changed type — skipped, never misdecoded */ + int32_t clamped; /* out-of-range values clamped to declared bounds */ + /* a key the TEXT form saw twice: last wins, and the repeat is counted + (docs/SPEC-TABLES.md §16.2). The wire never raises it — a body carrying an + id twice is legal input whose last occurrence wins, silently (§3). */ + int32_t duplicate; + int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ +} TableReport; + +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + +/* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- + + Static field descriptors for every type in the table closure: name, wire + id/kind, storage offset, bounds, ranges, enum names and branch guards — + enough to walk, print, diff, edit or bind any table value at runtime with + no schema files. _table_type() returns 's descriptor. */ + +struct TableTypeInfo; +struct TableFieldInfo; + +/* One arm of a union field: where its payload sits inside the union's storage + and what its payload looks like. The arm's NAME and its table-wire id ride + in the field's own vocabulary table at the same tag, so nothing is spelled + twice (docs/SPEC-TABLES.md §8). */ +typedef struct TableUnionArmInfo +{ + uint32_t offset; /* offsetof the arm's payload within the union storage */ + const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ +} TableUnionArmInfo; + +/* A union field's shape: the tag, and the arms indexed by it. Arms run + [0, enum_max]; index 0 is the EMPTY arm and carries no payload. */ +typedef struct TableUnionInfo +{ + uint32_t tag_offset; /* offsetof the tag within the union storage */ + uint32_t tag_size; /* sizeof the tag */ + const TableUnionArmInfo * arms; +} TableUnionInfo; + +/* ONE ENTRY OF A VOCABULARY, and the whole of what C++ spells as a pair of + captureless lambdas. An enum's values, a union's arms and a flags field's + BITS are each a named set indexed by [0, enum_max], so the set rides as an + ARRAY indexed the same way: no function per enum, and therefore no name per + enum for a schema to collide with (docs/SPEC-TABLES.md §11). + + name is the variant's spelling, or NULL for a value the declared set does + not name. id is the TABLE-WIRE id it rides under (§5) — the hash of the + variant's name, 0 for the reserved None and for the empty union arm. A + FLAGS field's variants are BIT POSITIONS and have no per-variant wire id + (§4), so has_ids is 0 there and 1 for an enum and a union: a vocabulary + with names and no ids is what says "flags". */ +typedef struct TableVariantInfo +{ + const char * name; + uint64_t id; +} TableVariantInfo; + +/* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// + block carries a doc column pointing at this one object, so absence costs a + unit no string data and a printer concatenates doc columns with no null + test. One definition per translation unit, and the descriptors of a unit + are one, so every absent doc compares equal by address. */ +static SCHEMA_UNUSED const char TableDocNone[1] = ""; + +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + +typedef struct TableFieldInfo +{ + const char * name; /* schema field name, e.g. "health" */ + const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ + const char * type_name; /* schema type name, e.g. "float32", "Grade" */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ + int is_array; /* fixed or counted array (bytes included) */ + int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ + int optional; /* a ?T field: a _present companion decides whether it rides */ + int32_t array_bound; /* array capacity / string max length; 0 for plain scalars */ + uint32_t offset; /* offsetof the storage member */ + uint32_t elem_size; /* sizeof the member (element size for arrays) */ + uint32_t count_offset; /* offsetof the _count/_length companion, or 0xffffffff */ + uint32_t present_offset; /* offsetof the _present companion, or 0xffffffff */ + const struct TableTypeInfo * table; /* nested table's descriptor, or NULL */ + int has_range; /* a declared [min, max] (int or float) */ + double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ + double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; + int64_t enum_max; /* enums: highest valid value (None = 0 always valid); + unions: the arm count (tag range [0, enum_max]); + flags: the highest declared BIT INDEX; else -1 */ + /* the vocabulary, indexed the same way enum_max bounds: an enum's value -> + (name, id), a union's tag -> (arm name, arm id), a FLAGS field's bit + index -> (variant name, 0). NULL for every other kind. */ + const TableVariantInfo * variants; + int has_variant_ids; /* 1 for an enum or a union, 0 for flags (§4) */ + /* an ENUM-KEYED array (docs/SPEC-TABLES.md §2.4): the array has one slot per + variant of key_type_name, the key k at index k-1, and its slots ride + under variant ids rather than positions. keys is the key's vocabulary, + indexed by the KEY — walk [0, array_bound) and ask about index + 1 to + print slots by name. NULL on every other field. */ + const char * key_type_name; + const TableVariantInfo * keys; + int64_t key_max; /* the key enum's highest value, bounding keys */ + /* union fields: the tag and its arms. NULL for every other kind. */ + const TableUnionInfo * arms; + const char * guard; /* branch guard, e.g. "at_rest" or "!at_rest"; "" if unguarded */ + /* what a PERSON wrote about the field (docs/SPEC-TABLES.md §8.1): the /// + block above it, verbatim (SPEC §4.1). It is TableDocNone when there is + none, never NULL. Its tags (SPEC §4.2) follow in declared order, and an + untagged field is 0 beside NULL. Static, constant-initialized, + allocating nothing. */ + const char * doc; + int32_t num_tags; + const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); +} TableFieldInfo; + +typedef struct TableTypeInfo +{ + const char * name; /* schema type name */ + uint32_t size; /* sizeof the storage struct */ + int32_t num_fields; + const TableFieldInfo * fields; + /* put one instance back at its declared defaults, in place. A generic + walker that fills a value has to be able to establish the defaults an + absent field takes, and it holds no type to spell — this is the one + thing the descriptors could not express without it. It is the void * + form of _reset and the same code. */ + void (*reset)( void * storage ); + /* the declaration's own doc and tags, on the same terms as a field's + (docs/SPEC-TABLES.md §8.1) */ + const char * doc; + int32_t num_tags; + const char * const * tags; +} TableTypeInfo; + + + +static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } +static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } +static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } +static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } + +#endif /* SCHEMA_BENCH_TABLE_PRIMITIVES */ + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void bench_bits_reset( BenchBits * value ); +static SCHEMA_UNUSED void schema_bench_bench_bits_reset_raw_( void * storage ); +static SCHEMA_UNUSED void bench_ints_reset( BenchInts * value ); +static SCHEMA_UNUSED void schema_bench_bench_ints_reset_raw_( void * storage ); +static SCHEMA_UNUSED void bench_mixed_reset( BenchMixed * value ); +static SCHEMA_UNUSED void schema_bench_bench_mixed_reset_raw_( void * storage ); +static SCHEMA_UNUSED void bench_packet_reset( BenchPacket * value ); +static SCHEMA_UNUSED void schema_bench_bench_packet_reset_raw_( void * storage ); +static SCHEMA_UNUSED void mixed_chat_event_reset( MixedChatEvent * value ); +static SCHEMA_UNUSED void schema_bench_mixed_chat_event_reset_raw_( void * storage ); +static SCHEMA_UNUSED void mixed_entity_reset( MixedEntity * value ); +static SCHEMA_UNUSED void schema_bench_mixed_entity_reset_raw_( void * storage ); +static SCHEMA_UNUSED void mixed_hit_event_reset( MixedHitEvent * value ); +static SCHEMA_UNUSED void schema_bench_mixed_hit_event_reset_raw_( void * storage ); +static SCHEMA_UNUSED void mixed_pickup_event_reset( MixedPickupEvent * value ); +static SCHEMA_UNUSED void schema_bench_mixed_pickup_event_reset_raw_( void * storage ); +static SCHEMA_UNUSED void mixed_stat_reset( MixedStat * value ); +static SCHEMA_UNUSED void schema_bench_mixed_stat_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void bench_bits_reset( BenchBits * value ) +{ + value->b7 = 0; + value->b13 = 0; + value->b23 = 0; + value->b3 = 0; + value->b32 = 0; + value->b11 = 0; + value->b19 = 0; + value->b48 = 0; +} + +static SCHEMA_UNUSED void schema_bench_bench_bits_reset_raw_( void * storage ) { bench_bits_reset( (BenchBits *) storage ); } + +extern const TableTypeInfo schema_bench_bench_bits_info_; +static SCHEMA_UNUSED const TableTypeInfo * bench_bits_table_type(void) { return &schema_bench_bench_bits_info_; } +static SCHEMA_UNUSED void bench_ints_reset( BenchInts * value ) +{ + value->f0 = 0; + value->f1 = 0; + value->f2 = 0; + value->f3 = 0; + value->f4 = 0; + value->f5 = 0; + value->f6 = 0; + value->f7 = 0; + value->f8 = 0; + value->f9 = 0; +} + +static SCHEMA_UNUSED void schema_bench_bench_ints_reset_raw_( void * storage ) { bench_ints_reset( (BenchInts *) storage ); } + +extern const TableTypeInfo schema_bench_bench_ints_info_; +static SCHEMA_UNUSED const TableTypeInfo * bench_ints_table_type(void) { return &schema_bench_bench_ints_info_; } +static SCHEMA_UNUSED void bench_mixed_reset( BenchMixed * value ) +{ + value->sequence = 0; + value->ack_sequence = 0; + value->ack_bits = 0; + value->session_id = 0; + value->client_id = 0; + value->nonce = 0; + value->world_time = 0; + value->frame_tick = 0; + value->server_time = 0; + mixed_entity_reset( &value->entities[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 8 ); i++ ) { value->entities[i] = value->entities[0]; } } + value->entities_count = 0; + mixed_stat_reset( &value->stats[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 80 ); i++ ) { value->stats[i] = value->stats[0]; } } + value->stats_count = 0; + memset( &value->game_event, 0, sizeof( value->game_event ) ); + memset( value->loadout, 0, sizeof( value->loadout ) ); + memset( value->player_name, 0, sizeof( value->player_name ) ); + value->player_name_length = 0; + memset( value->payload, 0, sizeof( value->payload ) ); + value->payload_length = 0; + value->aim_x = 0.0f; + value->aim_y = 0.0f; + value->aim_z = 0.0f; + value->recoil = 0.0f; + value->drift = 0.0; + value->wide_key = serialize_uint128_make( 0ull, 0ull ); + value->flux = serialize_int128_make( 0ull, 0ull ); + value->ping = 0; + value->crc_hint = 0; + value->has_extra = 1; + value->extra = 0; + value->idle_ticks = 0; +} + +static SCHEMA_UNUSED void schema_bench_bench_mixed_reset_raw_( void * storage ) { bench_mixed_reset( (BenchMixed *) storage ); } + +extern const TableTypeInfo schema_bench_bench_mixed_info_; +static SCHEMA_UNUSED const TableTypeInfo * bench_mixed_table_type(void) { return &schema_bench_bench_mixed_info_; } +static SCHEMA_UNUSED void bench_packet_reset( BenchPacket * value ) +{ + value->a = 0; + value->b = 0; + value->c = 0; + value->bits7 = 0; + value->bits13 = 0; + value->bits23 = 0; + value->flag = 0; + value->x = 0.0f; + value->y = 0.0f; + value->z = 0.0f; + value->big = 0; + memset( value->blob, 0, sizeof( value->blob ) ); +} + +static SCHEMA_UNUSED void schema_bench_bench_packet_reset_raw_( void * storage ) { bench_packet_reset( (BenchPacket *) storage ); } + +extern const TableTypeInfo schema_bench_bench_packet_info_; +static SCHEMA_UNUSED const TableTypeInfo * bench_packet_table_type(void) { return &schema_bench_bench_packet_info_; } +static SCHEMA_UNUSED void mixed_chat_event_reset( MixedChatEvent * value ) +{ + value->channel = 0; + value->speaker = 0; +} + +static SCHEMA_UNUSED void schema_bench_mixed_chat_event_reset_raw_( void * storage ) { mixed_chat_event_reset( (MixedChatEvent *) storage ); } + +extern const TableTypeInfo schema_bench_mixed_chat_event_info_; +static SCHEMA_UNUSED const TableTypeInfo * mixed_chat_event_table_type(void) { return &schema_bench_mixed_chat_event_info_; } +static SCHEMA_UNUSED void mixed_entity_reset( MixedEntity * value ) +{ + value->entity_id = 0; + value->pos_x = 0; + value->pos_y = 0; + value->pos_z = 0; + value->yaw = 0; + value->pitch = 0; + value->vel_x = 0; + value->vel_y = 0; + value->vel_z = 0; + value->health = 0; + value->weapon = MIXED_WEAPON_NONE; + value->damage = 0; + value->moving = 0; + value->firing = 0; +} + +static SCHEMA_UNUSED void schema_bench_mixed_entity_reset_raw_( void * storage ) { mixed_entity_reset( (MixedEntity *) storage ); } + +extern const TableTypeInfo schema_bench_mixed_entity_info_; +static SCHEMA_UNUSED const TableTypeInfo * mixed_entity_table_type(void) { return &schema_bench_mixed_entity_info_; } +static SCHEMA_UNUSED void mixed_hit_event_reset( MixedHitEvent * value ) +{ + value->target_id = 0; + value->damage = 0; + value->hit_kind = 0; + value->crit = 0; +} + +static SCHEMA_UNUSED void schema_bench_mixed_hit_event_reset_raw_( void * storage ) { mixed_hit_event_reset( (MixedHitEvent *) storage ); } + +extern const TableTypeInfo schema_bench_mixed_hit_event_info_; +static SCHEMA_UNUSED const TableTypeInfo * mixed_hit_event_table_type(void) { return &schema_bench_mixed_hit_event_info_; } +static SCHEMA_UNUSED void mixed_pickup_event_reset( MixedPickupEvent * value ) +{ + value->item_id = 0; + value->amount = 0; +} + +static SCHEMA_UNUSED void schema_bench_mixed_pickup_event_reset_raw_( void * storage ) { mixed_pickup_event_reset( (MixedPickupEvent *) storage ); } + +extern const TableTypeInfo schema_bench_mixed_pickup_event_info_; +static SCHEMA_UNUSED const TableTypeInfo * mixed_pickup_event_table_type(void) { return &schema_bench_mixed_pickup_event_info_; } +static SCHEMA_UNUSED void mixed_stat_reset( MixedStat * value ) +{ + value->stat_id = 0; + value->delta = 0; +} + +static SCHEMA_UNUSED void schema_bench_mixed_stat_reset_raw_( void * storage ) { mixed_stat_reset( (MixedStat *) storage ); } + +extern const TableTypeInfo schema_bench_mixed_stat_info_; +static SCHEMA_UNUSED const TableTypeInfo * mixed_stat_table_type(void) { return &schema_bench_mixed_stat_info_; } +const UnitViewInfo * schema_bench_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_bench_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/generated/bench/c/RealworldView.c b/generated/bench/c/RealworldView.c new file mode 100644 index 000000000..d7972d051 --- /dev/null +++ b/generated/bench/c/RealworldView.c @@ -0,0 +1,171 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "RealworldView.h" +static const TableWideRange schema_realworld_real_packet_f016_fixed_outside_range_ = { { 18446744073671802880ull, 18446744073709551615ull }, { 37748736ull, 0ull } }; +static const TableWideRange schema_realworld_real_packet_f021_ufixed_outside_range_ = { { 0ull, 0ull }, { 102977536ull, 0ull } }; +static const TableWideRange schema_realworld_real_packet_f025_fixed_outside_range_ = { { 18446744073709521152ull, 18446744073709551615ull }, { 30464ull, 0ull } }; +static const TableVariantInfo schema_realworld_real_packet_f036_enum_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Idle", 0x0000000000000000ull }, + { "Active", 0x0000000000000000ull }, + { "Combat", 0x0000000000000000ull }, + { "Docked", 0x0000000000000000ull }, + { "Warping", 0x0000000000000000ull }, +}; +static const TableWideRange schema_realworld_real_packet_f040_fixed_outside_range_ = { { 18446744073709531136ull, 18446744073709551615ull }, { 20480ull, 0ull } }; +static const TableWideRange schema_realworld_real_packet_f049_ufixed_outside_range_ = { { 0ull, 0ull }, { 49152ull, 0ull } }; +static const TableWideRange schema_realworld_real_packet_f066_ufixed_outside_range_ = { { 0ull, 0ull }, { 32768ull, 0ull } }; +static const TableVariantInfo schema_realworld_real_packet_f083_enum_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Idle", 0x0000000000000000ull }, + { "Active", 0x0000000000000000ull }, + { "Combat", 0x0000000000000000ull }, + { "Docked", 0x0000000000000000ull }, + { "Warping", 0x0000000000000000ull }, +}; +static const TableWideRange schema_realworld_real_packet_f084_ufixed_outside_range_ = { { 0ull, 0ull }, { 128ull, 0ull } }; +static const TableVariantInfo schema_realworld_real_packet_f091_flags_outside_variants_[] = { + { "Shielded", 0 }, + { "Cloaked", 0 }, + { "Overheated", 0 }, + { "LowPower", 0 }, + { "Jamming", 0 }, +}; +static const TableWideRange schema_realworld_real_packet_f095_fixed_outside_range_ = { { 18446744073606201344ull, 18446744073709551615ull }, { 103350272ull, 0ull } }; +static const TableFieldInfo schema_realworld_real_packet_fields_[] = { + { "f001_int", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f001_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f001_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -805495.0, 805495.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f002_f64", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f002_f64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f002_f64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f003_int", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f003_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f003_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -835897.0, 835897.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f004_cf32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f004_cf32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f004_cf32 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f005_uint", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f005_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f005_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7316.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f006_int", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f006_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f006_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -1513.0, 1513.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f007_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f007_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f007_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f008_u64", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f008_u64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f008_u64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f009_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f009_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f009_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -22.0, 22.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f010_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f010_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f010_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f011_bits", NULL, "bits(10)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f011_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f011_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1023.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f012_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f012_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f012_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f013_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f013_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f013_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f012_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f014_uint", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f014_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f014_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 775.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f012_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f015_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f015_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f015_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -21.0, 21.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f012_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f016_fixed", NULL, "fixed(12, 20)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f016_fixed ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f016_fixed ), 0xffffffffu, 0xffffffffu, NULL, 1, -36.0, 36.0, &schema_realworld_real_packet_f016_fixed_outside_range_, 20, -1, NULL, 0, NULL, NULL, -1, NULL, "f012_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f017_uint", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f017_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f017_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4606.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f012_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f018_int", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f018_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f018_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -834.0, 834.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f019_f64", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f019_f64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f019_f64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f020_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f020_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f020_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f021_ufixed", NULL, "ufixed(20, 12)", 0x0000000000000000ull, 27, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f021_ufixed ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f021_ufixed ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 25141.0, &schema_realworld_real_packet_f021_ufixed_outside_range_, 12, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f022_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f022_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f022_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f023_bits", NULL, "bits(25)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f023_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f023_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.3554431e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f024_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f024_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f024_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f025_fixed", NULL, "fixed(8, 8)", 0x0000000000000000ull, 21, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f025_fixed ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f025_fixed ), 0xffffffffu, 0xffffffffu, NULL, 1, -119.0, 119.0, &schema_realworld_real_packet_f025_fixed_outside_range_, 8, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f026_bits", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f026_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f026_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f027_cf32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f027_cf32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f027_cf32 ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.0, 2.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f028_bits", NULL, "bits(4)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f028_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f028_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 15.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f029_i64", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f029_i64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f029_i64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f030_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f030_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f030_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f031_bits", NULL, "bits(1)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f031_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f031_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f032_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f032_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f032_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -3.0, 3.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f033_uint", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f033_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f033_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 142780.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f034_uint", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f034_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f034_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 14149.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f035_bits", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f035_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f035_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f036_enum", NULL, "PacketMode", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f036_enum ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f036_enum ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 5, schema_realworld_real_packet_f036_enum_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f037_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f037_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f037_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f038_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f038_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f038_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f039_bits", NULL, "bits(19)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f039_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f039_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 524287.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f040_fixed", NULL, "fixed(4, 12)", 0x0000000000000000ull, 21, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f040_fixed ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f040_fixed ), 0xffffffffu, 0xffffffffu, NULL, 1, -5.0, 5.0, &schema_realworld_real_packet_f040_fixed_outside_range_, 12, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f041_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f041_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f041_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -55.0, 55.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f042_bits", NULL, "bits(30)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f042_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f042_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.073741823e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f043_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f043_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f043_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f044_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f044_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f044_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f043_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f045_bits", NULL, "bits(12)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f045_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f045_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f043_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f046_uint", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f046_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f046_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 76063.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f043_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f047_int", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f047_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f047_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -430976.0, 430976.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f043_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f048_f64", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f048_f64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f048_f64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f049_ufixed", NULL, "ufixed(2, 14)", 0x0000000000000000ull, 26, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f049_ufixed ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f049_ufixed ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.0, &schema_realworld_real_packet_f049_ufixed_outside_range_, 14, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f050_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f050_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f050_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f051_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f051_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f051_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f050_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f052_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f052_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f052_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -57.0, 57.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f050_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f053_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f053_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f053_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f050_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f054_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f054_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f054_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -35.0, 35.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f050_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f055_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f055_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f055_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f056_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f056_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f056_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -13.0, 13.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f057_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f057_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f057_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -15.0, 15.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f058_f32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f058_f32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f058_f32 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f059_f64", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f059_f64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f059_f64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f060_bits", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f060_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f060_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f061_cf32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f061_cf32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f061_cf32 ), 0xffffffffu, 0xffffffffu, NULL, 1, -90.0, 90.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f062_uint", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f062_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f062_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 503.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f063_i64", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f063_i64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f063_i64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f064_uint", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f064_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f064_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 299.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f065_cf32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f065_cf32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f065_cf32 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 30.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f066_ufixed", NULL, "ufixed(2, 14)", 0x0000000000000000ull, 26, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f066_ufixed ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f066_ufixed ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.0, &schema_realworld_real_packet_f066_ufixed_outside_range_, 14, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f067_cf32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f067_cf32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f067_cf32 ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f068_cf32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f068_cf32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f068_cf32 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f069_bits", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f069_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f069_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f070_uint", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f070_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f070_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f071_cf32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f071_cf32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f071_cf32 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f072_cf32", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f072_cf32 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f072_cf32 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f073_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f073_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f073_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -4.0, 4.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f074_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f074_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f074_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f075_u64", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f075_u64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f075_u64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f074_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f076_int", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f076_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f076_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -26218.0, 26218.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f074_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f077_int", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f077_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f077_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -17.0, 17.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f074_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f078_bits", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f078_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f078_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f074_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f079_uint", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f079_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f079_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 17.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "f074_bool", TableDocNone, 0, NULL, 0, NULL }, + { "f080_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f080_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f080_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f081_bits", NULL, "bits(29)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f081_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f081_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 5.36870911e+08, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f082_bits", NULL, "bits(25)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f082_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f082_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.3554431e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f083_enum", NULL, "PacketMode", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f083_enum ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f083_enum ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 5, schema_realworld_real_packet_f083_enum_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f084_ufixed", NULL, "ufixed(1, 7)", 0x0000000000000000ull, 25, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f084_ufixed ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f084_ufixed ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.0, &schema_realworld_real_packet_f084_ufixed_outside_range_, 7, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f085_bits", NULL, "bits(21)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f085_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f085_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.097151e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f086_uint", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f086_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f086_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 399.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f087_f64", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f087_f64 ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f087_f64 ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f088_int", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f088_int ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f088_int ), 0xffffffffu, 0xffffffffu, NULL, 1, -694.0, 694.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f089_bits", NULL, "bits(48)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f089_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f089_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f090_uint", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f090_uint ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f090_uint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 214.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f091_flags", NULL, "PacketFlags", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f091_flags ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f091_flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_realworld_real_packet_f091_flags_outside_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f092_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f092_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f092_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f093_bits", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f093_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f093_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f094_bool", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f094_bool ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f094_bool ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f095_fixed", NULL, "fixed(16, 16)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f095_fixed ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f095_fixed ), 0xffffffffu, 0xffffffffu, NULL, 1, -1577.0, 1577.0, &schema_realworld_real_packet_f095_fixed_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f096_bits", NULL, "bits(18)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f096_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f096_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 262143.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f097_bits", NULL, "bits(12)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RealPacket, f097_bits ), (uint32_t) sizeof( ( (RealPacket *) 0 )->f097_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_realworld_real_packet_info_ = { "RealPacket", (uint32_t) sizeof( RealPacket ), 97, schema_realworld_real_packet_fields_, schema_realworld_real_packet_reset_raw_, TableDocNone, 0, NULL }; + +const UnitViewInfo * schema_realworld_unit_view_(void) +{ + static const ViewVariant PacketMode_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Idle", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Active", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Combat", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Docked", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "Warping", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant PacketFlags_view_variants[] = { + { 0, "Shielded", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Cloaked", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Overheated", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "LowPower", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Jamming", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "PacketMode", "RealWorld.schema", 5, 8, 6, PacketMode_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary flags_decls[] = { + { "PacketFlags", "RealWorld.schema", 4, 8, 5, PacketFlags_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "RealPacket", "RealWorld.schema", 0, &schema_realworld_real_packet_info_, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "realworld", 0x1922430092518648ull, + 1, types, + 0, NULL, + 1, enums, + 1, flags_decls, + 0, NULL, + 0, NULL }; + return &info; +} diff --git a/generated/bench/c/RealworldView.h b/generated/bench/c/RealworldView.h new file mode 100644 index 000000000..f5e3a444e --- /dev/null +++ b/generated/bench/c/RealworldView.h @@ -0,0 +1,433 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_REALWORLD_VIEW_H +#define SCHEMA_REALWORLD_VIEW_H +#include +#include +#include +#include "RealWorld.h" +#ifndef SCHEMA_REALWORLD_TABLE_PRIMITIVES +#define SCHEMA_REALWORLD_TABLE_PRIMITIVES + +/* THE CODEC DOES NOT DEPEND ON THE COMPILER'S INLINING BUDGET. A table of a + realistic field count emits one large body per type, and the cursor a body + writes through lives in the caller's TableWriter: across a call boundary that + cursor round-trips through memory, and a uint8_t * store may alias the writer + itself, so every put reloads it. When a budget runs out mid-body the codec + silently degrades to that shape. Forcing the primitives and the fixed-class + bodies inline is what keeps the cursor in registers and lets adjacent + constant framing bytes merge into one store. + + The spelling is FEATURE TESTED rather than assumed, exactly as the packet + emitter's own inlining demand is (SPEC §6.1's C column). + + IT CARRIES THE PACKAGE for the reason every other name here does, and that + reason is the LINKER rather than the preprocessor: two units of the same + schema family link into one program — two generations of one schema is the + case the conformance driver itself is — so nothing this backend emits may + be spelled the same way by two packages. They do NOT share a translation + unit and cannot: C has no namespace, so two units' headers in one TU + redefine TableReport, TableWriter and every other runtime name, which is the + packet emitter's standing limit (SPEC §6.1) and not something tables + changed. A per-package macro is what keeps the LINK legal. */ +#ifndef SCHEMA_REALWORLD_TABLE_INLINE +#if defined( _MSC_VER ) +#define SCHEMA_REALWORLD_TABLE_INLINE __forceinline +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_REALWORLD_TABLE_INLINE inline __attribute__(( always_inline )) +#else +#define SCHEMA_REALWORLD_TABLE_INLINE inline +#endif +#endif + +/* A COMPILE-TIME assertion under -std=c99, which has none of its own. C11's + _Static_assert carries the message into the build log verbatim and is used + wherever the compiler has it; everywhere else the negative array bound is + the same refusal with a tag a reader can find. The tag is unique per record + and field, and each record is asserted by the ONE header that declares it, + so no two of these ever meet. */ +#ifndef SCHEMA_TABLE_STATIC_ASSERT +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) _Static_assert( cond, message ) +#elif defined( __cplusplus ) && __cplusplus >= 201103L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) static_assert( cond, message ) +#else +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) typedef char schema_table_assert_##tag[ ( cond ) ? 1 : -1 ] +#endif +#endif + +/* A record's ALIGNMENT, which the cook's layout contract asserts and which the + cook's Open compares the header's alignment word against (§7.1, §20.3). + C99 has no alignof either; C11's _Alignof, the two GNU spellings and MSVC's + cover every compiler this repo builds under, and the last form is the + portable definition of the same number. */ +#ifndef SCHEMA_TABLE_ALIGNOF +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_ALIGNOF( T ) _Alignof( T ) +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof__( T ) +#elif defined( _MSC_VER ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof( T ) +#else +#define SCHEMA_TABLE_ALIGNOF( T ) ( (size_t) offsetof( struct { char schema_pad_; T schema_member_; }, schema_member_ ) ) +#endif +#endif + +/* The table-wire read report — the permissive contract's ledger. Silence + (all zero) means the data matched this reader's schema exactly. */ +typedef struct TableReport +{ + int32_t unknown; /* unknown field ids skipped (newer data) */ + int32_t kind_mismatch; /* known id, changed type — skipped, never misdecoded */ + int32_t clamped; /* out-of-range values clamped to declared bounds */ + /* a key the TEXT form saw twice: last wins, and the repeat is counted + (docs/SPEC-TABLES.md §16.2). The wire never raises it — a body carrying an + id twice is legal input whose last occurrence wins, silently (§3). */ + int32_t duplicate; + int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ +} TableReport; + +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + +/* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- + + Static field descriptors for every type in the table closure: name, wire + id/kind, storage offset, bounds, ranges, enum names and branch guards — + enough to walk, print, diff, edit or bind any table value at runtime with + no schema files. _table_type() returns 's descriptor. */ + +struct TableTypeInfo; +struct TableFieldInfo; + +/* One arm of a union field: where its payload sits inside the union's storage + and what its payload looks like. The arm's NAME and its table-wire id ride + in the field's own vocabulary table at the same tag, so nothing is spelled + twice (docs/SPEC-TABLES.md §8). */ +typedef struct TableUnionArmInfo +{ + uint32_t offset; /* offsetof the arm's payload within the union storage */ + const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ +} TableUnionArmInfo; + +/* A union field's shape: the tag, and the arms indexed by it. Arms run + [0, enum_max]; index 0 is the EMPTY arm and carries no payload. */ +typedef struct TableUnionInfo +{ + uint32_t tag_offset; /* offsetof the tag within the union storage */ + uint32_t tag_size; /* sizeof the tag */ + const TableUnionArmInfo * arms; +} TableUnionInfo; + +/* ONE ENTRY OF A VOCABULARY, and the whole of what C++ spells as a pair of + captureless lambdas. An enum's values, a union's arms and a flags field's + BITS are each a named set indexed by [0, enum_max], so the set rides as an + ARRAY indexed the same way: no function per enum, and therefore no name per + enum for a schema to collide with (docs/SPEC-TABLES.md §11). + + name is the variant's spelling, or NULL for a value the declared set does + not name. id is the TABLE-WIRE id it rides under (§5) — the hash of the + variant's name, 0 for the reserved None and for the empty union arm. A + FLAGS field's variants are BIT POSITIONS and have no per-variant wire id + (§4), so has_ids is 0 there and 1 for an enum and a union: a vocabulary + with names and no ids is what says "flags". */ +typedef struct TableVariantInfo +{ + const char * name; + uint64_t id; +} TableVariantInfo; + +/* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// + block carries a doc column pointing at this one object, so absence costs a + unit no string data and a printer concatenates doc columns with no null + test. One definition per translation unit, and the descriptors of a unit + are one, so every absent doc compares equal by address. */ +static SCHEMA_UNUSED const char TableDocNone[1] = ""; + +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + +typedef struct TableFieldInfo +{ + const char * name; /* schema field name, e.g. "health" */ + const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ + const char * type_name; /* schema type name, e.g. "float32", "Grade" */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ + int is_array; /* fixed or counted array (bytes included) */ + int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ + int optional; /* a ?T field: a _present companion decides whether it rides */ + int32_t array_bound; /* array capacity / string max length; 0 for plain scalars */ + uint32_t offset; /* offsetof the storage member */ + uint32_t elem_size; /* sizeof the member (element size for arrays) */ + uint32_t count_offset; /* offsetof the _count/_length companion, or 0xffffffff */ + uint32_t present_offset; /* offsetof the _present companion, or 0xffffffff */ + const struct TableTypeInfo * table; /* nested table's descriptor, or NULL */ + int has_range; /* a declared [min, max] (int or float) */ + double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ + double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; + int64_t enum_max; /* enums: highest valid value (None = 0 always valid); + unions: the arm count (tag range [0, enum_max]); + flags: the highest declared BIT INDEX; else -1 */ + /* the vocabulary, indexed the same way enum_max bounds: an enum's value -> + (name, id), a union's tag -> (arm name, arm id), a FLAGS field's bit + index -> (variant name, 0). NULL for every other kind. */ + const TableVariantInfo * variants; + int has_variant_ids; /* 1 for an enum or a union, 0 for flags (§4) */ + /* an ENUM-KEYED array (docs/SPEC-TABLES.md §2.4): the array has one slot per + variant of key_type_name, the key k at index k-1, and its slots ride + under variant ids rather than positions. keys is the key's vocabulary, + indexed by the KEY — walk [0, array_bound) and ask about index + 1 to + print slots by name. NULL on every other field. */ + const char * key_type_name; + const TableVariantInfo * keys; + int64_t key_max; /* the key enum's highest value, bounding keys */ + /* union fields: the tag and its arms. NULL for every other kind. */ + const TableUnionInfo * arms; + const char * guard; /* branch guard, e.g. "at_rest" or "!at_rest"; "" if unguarded */ + /* what a PERSON wrote about the field (docs/SPEC-TABLES.md §8.1): the /// + block above it, verbatim (SPEC §4.1). It is TableDocNone when there is + none, never NULL. Its tags (SPEC §4.2) follow in declared order, and an + untagged field is 0 beside NULL. Static, constant-initialized, + allocating nothing. */ + const char * doc; + int32_t num_tags; + const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); +} TableFieldInfo; + +typedef struct TableTypeInfo +{ + const char * name; /* schema type name */ + uint32_t size; /* sizeof the storage struct */ + int32_t num_fields; + const TableFieldInfo * fields; + /* put one instance back at its declared defaults, in place. A generic + walker that fills a value has to be able to establish the defaults an + absent field takes, and it holds no type to spell — this is the one + thing the descriptors could not express without it. It is the void * + form of _reset and the same code. */ + void (*reset)( void * storage ); + /* the declaration's own doc and tags, on the same terms as a field's + (docs/SPEC-TABLES.md §8.1) */ + const char * doc; + int32_t num_tags; + const char * const * tags; +} TableTypeInfo; + + + +static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } +static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } +static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } +static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } + +#endif /* SCHEMA_REALWORLD_TABLE_PRIMITIVES */ + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void real_packet_reset( RealPacket * value ); +static SCHEMA_UNUSED void schema_realworld_real_packet_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void real_packet_reset( RealPacket * value ) +{ + value->f001_int = 0; + value->f002_f64 = 0.0; + value->f003_int = 0; + value->f004_cf32 = 0.0f; + value->f005_uint = 0; + value->f006_int = 0; + value->f007_f32 = 0.0f; + value->f008_u64 = 0; + value->f009_int = 0; + value->f010_f32 = 0.0f; + value->f011_bits = 0; + value->f012_bool = 1; + value->f013_f32 = 0.0f; + value->f014_uint = 0; + value->f015_int = 0; + value->f016_fixed = 0; + value->f017_uint = 0; + value->f018_int = 0; + value->f019_f64 = 0.0; + value->f020_f32 = 0.0f; + value->f021_ufixed = 0; + value->f022_f32 = 0.0f; + value->f023_bits = 0; + value->f024_f32 = 0.0f; + value->f025_fixed = 0; + value->f026_bits = 0; + value->f027_cf32 = 0.0f; + value->f028_bits = 0; + value->f029_i64 = 0; + value->f030_f32 = 0.0f; + value->f031_bits = 0; + value->f032_int = 0; + value->f033_uint = 0; + value->f034_uint = 0; + value->f035_bits = 0; + value->f036_enum = PACKET_MODE_NONE; + value->f037_bool = 0; + value->f038_bool = 0; + value->f039_bits = 0; + value->f040_fixed = 0; + value->f041_int = 0; + value->f042_bits = 0; + value->f043_bool = 0; + value->f044_f32 = 0.0f; + value->f045_bits = 0; + value->f046_uint = 0; + value->f047_int = 0; + value->f048_f64 = 0.0; + value->f049_ufixed = 0; + value->f050_bool = 1; + value->f051_bool = 0; + value->f052_int = 0; + value->f053_f32 = 0.0f; + value->f054_int = 0; + value->f055_bool = 0; + value->f056_int = 0; + value->f057_int = 0; + value->f058_f32 = 0.0f; + value->f059_f64 = 0.0; + value->f060_bits = 0; + value->f061_cf32 = 0.0f; + value->f062_uint = 0; + value->f063_i64 = 0; + value->f064_uint = 0; + value->f065_cf32 = 0.0f; + value->f066_ufixed = 0; + value->f067_cf32 = 0.0f; + value->f068_cf32 = 0.0f; + value->f069_bits = 0; + value->f070_uint = 0; + value->f071_cf32 = 0.0f; + value->f072_cf32 = 0.0f; + value->f073_int = 0; + value->f074_bool = 0; + value->f075_u64 = 0; + value->f076_int = 0; + value->f077_int = 0; + value->f078_bits = 0; + value->f079_uint = 0; + value->f080_bool = 0; + value->f081_bits = 0; + value->f082_bits = 0; + value->f083_enum = PACKET_MODE_NONE; + value->f084_ufixed = 0; + value->f085_bits = 0; + value->f086_uint = 0; + value->f087_f64 = 0.0; + value->f088_int = 0; + value->f089_bits = 0; + value->f090_uint = 0; + value->f091_flags = 0; + value->f092_bool = 0; + value->f093_bits = 0; + value->f094_bool = 0; + value->f095_fixed = 0; + value->f096_bits = 0; + value->f097_bits = 0; +} + +static SCHEMA_UNUSED void schema_realworld_real_packet_reset_raw_( void * storage ) { real_packet_reset( (RealPacket *) storage ); } + +extern const TableTypeInfo schema_realworld_real_packet_info_; +static SCHEMA_UNUSED const TableTypeInfo * real_packet_table_type(void) { return &schema_realworld_real_packet_info_; } +const UnitViewInfo * schema_realworld_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_realworld_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/generated/bench/tables/c/BenchTableBlock.c b/generated/bench/tables/c/BenchTableBlock.c index c05b7f546..bce895b0b 100644 --- a/generated/bench/tables/c/BenchTableBlock.c +++ b/generated/bench/tables/c/BenchTableBlock.c @@ -13,46 +13,33 @@ /* ---- the block form of table TableEntity: the open path and the descriptors ---- */ -int schema_benchtable_table_entity_block_open_( TableEntityBlock * block, void * base, int64_t bytes ) +static int schema_benchtable_table_entity_block_open_check_(const void * base,int64_t bytes,int64_t * used_out,TableRefuseReason * reason) { - const uint8_t * raw; - uint64_t magic; - TableEntityBlockProjection * projection; - int64_t used, padding; - block->base = NULL; - block->projection = NULL; - block->bytes = 0; - if ( base == NULL || bytes < 88 ) { return 0; } - if ( ( (uintptr_t) base % 64 ) != 0 ) { return 0; } /* the base's alignment */ - raw = (const uint8_t *) base; - magic = table_block_read64( raw ); - if ( magic != table_block_magic ) - { - /* a byte-swapped magic is a FOREIGN BYTE ORDER, and anything else is - not a block at all. Both refuse; the distinction is here so a - reader of this code knows the check covers the order too. */ - (void) table_block_byteswap64( magic ); - return 0; - } - if ( table_block_read64( raw + 8 ) != SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE ) { return 0; } - if ( table_block_read64( raw + 16 ) != table_block_byte_order ) - { - return 0; /* a block of the other byte order: the fix-up path is a named obligation */ - } - projection = (TableEntityBlockProjection *) base; - used = 88; - /* the used extent, rounded to 64 WITHOUT the rounding itself carrying past - the top of the type: used is already inside bytes, and the padding is - paid out of the slack that is left rather than added and compared after. */ - padding = ( 64 - ( used % 64 ) ) % 64; - if ( padding > bytes - used ) { return 0; } - used += padding; - block->base = (uint8_t *) base; - block->projection = projection; - block->bytes = used; - return 1; + const uint8_t * raw=(const uint8_t *)base; uint64_t magic; int64_t used=88, padding; + if(base==NULL) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE)!=NULL; } + if(bytes<88) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED)!=NULL; } + magic=table_block_read64(raw); + if(magic!=table_block_magic) { return table_cook_refuse(reason,magic==table_block_byteswap64(table_block_magic) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK)!=NULL; } + if(table_block_read64(raw+16)!=table_block_byte_order) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK)!=NULL; } + if(table_block_read64(raw+8)!=SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION)!=NULL; } + padding=(64-used%64)%64; if(padding>bytes-used) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED)!=NULL; } + used+=padding; if((uintptr_t)base%64!=0) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE)!=NULL; } + *used_out=used; return 1; } - +int schema_benchtable_table_entity_block_open_ex_(TableEntityBlock * block,void * base,int64_t bytes,TableRefuseReason * reason) +{ + int64_t used=0; block->base=NULL; block->projection=NULL; block->bytes=0; + if(!schema_benchtable_table_entity_block_open_check_(base,bytes,&used,reason)) { return 0; } + block->base=(uint8_t *)base; block->projection=(TableEntityBlockProjection *)base; block->bytes=used; return 1; +} +int schema_benchtable_table_entity_block_open_const_ex_(TableEntityBlockConst * block,const void * base,int64_t bytes,TableRefuseReason * reason) +{ + int64_t used=0; block->base=NULL; block->projection=NULL; block->bytes=0; + if(!schema_benchtable_table_entity_block_open_check_(base,bytes,&used,reason)) { return 0; } + block->base=(const uint8_t *)base; block->projection=(const TableEntityBlockProjection *)base; block->bytes=used; return 1; +} +int schema_benchtable_table_entity_block_open_(TableEntityBlock * block,void * base,int64_t bytes) +{ return schema_benchtable_table_entity_block_open_ex_(block,base,bytes,NULL); } /* Every record's field table, concatenated: the projection's, then each row's. One name for the whole graph (docs/SPEC-TABLES.md §11). */ static const TableBlockFieldInfo schema_benchtable_table_entity_block_fields_[] = { @@ -77,46 +64,33 @@ const TableBlockInfo schema_benchtable_table_entity_block_info_ = { "TableEntity /* ---- the block form of table TableStat: the open path and the descriptors ---- */ -int schema_benchtable_table_stat_block_open_( TableStatBlock * block, void * base, int64_t bytes ) +static int schema_benchtable_table_stat_block_open_check_(const void * base,int64_t bytes,int64_t * used_out,TableRefuseReason * reason) { - const uint8_t * raw; - uint64_t magic; - TableStatBlockProjection * projection; - int64_t used, padding; - block->base = NULL; - block->projection = NULL; - block->bytes = 0; - if ( base == NULL || bytes < 32 ) { return 0; } - if ( ( (uintptr_t) base % 64 ) != 0 ) { return 0; } /* the base's alignment */ - raw = (const uint8_t *) base; - magic = table_block_read64( raw ); - if ( magic != table_block_magic ) - { - /* a byte-swapped magic is a FOREIGN BYTE ORDER, and anything else is - not a block at all. Both refuse; the distinction is here so a - reader of this code knows the check covers the order too. */ - (void) table_block_byteswap64( magic ); - return 0; - } - if ( table_block_read64( raw + 8 ) != SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE ) { return 0; } - if ( table_block_read64( raw + 16 ) != table_block_byte_order ) - { - return 0; /* a block of the other byte order: the fix-up path is a named obligation */ - } - projection = (TableStatBlockProjection *) base; - used = 32; - /* the used extent, rounded to 64 WITHOUT the rounding itself carrying past - the top of the type: used is already inside bytes, and the padding is - paid out of the slack that is left rather than added and compared after. */ - padding = ( 64 - ( used % 64 ) ) % 64; - if ( padding > bytes - used ) { return 0; } - used += padding; - block->base = (uint8_t *) base; - block->projection = projection; - block->bytes = used; - return 1; + const uint8_t * raw=(const uint8_t *)base; uint64_t magic; int64_t used=32, padding; + if(base==NULL) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE)!=NULL; } + if(bytes<32) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED)!=NULL; } + magic=table_block_read64(raw); + if(magic!=table_block_magic) { return table_cook_refuse(reason,magic==table_block_byteswap64(table_block_magic) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK)!=NULL; } + if(table_block_read64(raw+16)!=table_block_byte_order) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK)!=NULL; } + if(table_block_read64(raw+8)!=SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION)!=NULL; } + padding=(64-used%64)%64; if(padding>bytes-used) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED)!=NULL; } + used+=padding; if((uintptr_t)base%64!=0) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE)!=NULL; } + *used_out=used; return 1; } - +int schema_benchtable_table_stat_block_open_ex_(TableStatBlock * block,void * base,int64_t bytes,TableRefuseReason * reason) +{ + int64_t used=0; block->base=NULL; block->projection=NULL; block->bytes=0; + if(!schema_benchtable_table_stat_block_open_check_(base,bytes,&used,reason)) { return 0; } + block->base=(uint8_t *)base; block->projection=(TableStatBlockProjection *)base; block->bytes=used; return 1; +} +int schema_benchtable_table_stat_block_open_const_ex_(TableStatBlockConst * block,const void * base,int64_t bytes,TableRefuseReason * reason) +{ + int64_t used=0; block->base=NULL; block->projection=NULL; block->bytes=0; + if(!schema_benchtable_table_stat_block_open_check_(base,bytes,&used,reason)) { return 0; } + block->base=(const uint8_t *)base; block->projection=(const TableStatBlockProjection *)base; block->bytes=used; return 1; +} +int schema_benchtable_table_stat_block_open_(TableStatBlock * block,void * base,int64_t bytes) +{ return schema_benchtable_table_stat_block_open_ex_(block,base,bytes,NULL); } /* Every record's field table, concatenated: the projection's, then each row's. One name for the whole graph (docs/SPEC-TABLES.md §11). */ static const TableBlockFieldInfo schema_benchtable_table_stat_block_fields_[] = { diff --git a/generated/bench/tables/c/BenchTableBlock.h b/generated/bench/tables/c/BenchTableBlock.h index 6c8342e4d..5826b9591 100644 --- a/generated/bench/tables/c/BenchTableBlock.h +++ b/generated/bench/tables/c/BenchTableBlock.h @@ -46,6 +46,39 @@ extern "C" { #endif /* SCHEMA_BENCHTABLE_BUILD_VERSION */ + +#ifndef SCHEMA_TABLE_ALLOCATOR_RUNTIME +#define SCHEMA_TABLE_ALLOCATOR_RUNTIME +#ifndef schema_allocate +#include +#define schema_allocate(bytes) calloc(1,(size_t)(bytes)) +#endif +#ifndef schema_release +#include +#define schema_release(pointer) free(pointer) +#endif +typedef struct TableAllocator +{ + void * (*alloc)(void * context,int64_t bytes); + void (*free)(void * context,void * pointer); + void * context; +} TableAllocator; +static SCHEMA_UNUSED void * table_default_allocate(void * context,int64_t bytes) +{ (void)context; return bytes>0 && (uint64_t)bytes<=SIZE_MAX ? schema_allocate(bytes) : NULL; } +static SCHEMA_UNUSED void table_default_release(void * context,void * pointer) +{ (void)context; schema_release(pointer); } +static SCHEMA_UNUSED TableAllocator table_default_allocator(void) +{ + TableAllocator allocator={table_default_allocate,table_default_release,NULL}; return allocator; +} +static SCHEMA_UNUSED void * table_allocate(TableAllocator allocator,int64_t bytes) +{ + if(allocator.alloc==NULL || allocator.free==NULL || bytes<=0 || (uint64_t)bytes>SIZE_MAX) { return NULL; } + return allocator.alloc(allocator.context,bytes); +} +static SCHEMA_UNUSED void table_release(TableAllocator allocator,void * pointer) +{ if(allocator.free!=NULL) { allocator.free(allocator.context,pointer); } } +#endif #ifndef SCHEMA_BENCHTABLE_BLOCK_PRIMITIVES #define SCHEMA_BENCHTABLE_BLOCK_PRIMITIVES @@ -70,17 +103,12 @@ typedef struct TableBlockTriple The FILL path allocates nothing and takes no lock, which is the obligation §19.1 states; "the block form never allocates" would be a claim about who owns the extent, and the answer is that the caller does. */ -typedef struct TableBlockAllocator -{ - void * ( *alloc )( void * context, int64_t bytes ); - void ( *free )( void * context, void * pointer ); - void * context; -} TableBlockAllocator; +typedef TableAllocator TableBlockAllocator; /* The default triple, for a caller that has no allocator of its own to hand in. Nothing in the generated surface reaches for it: a caller names it. */ -static SCHEMA_UNUSED void * table_block_default_alloc( void * context, int64_t bytes ) { (void) context; return malloc( (size_t) bytes ); } -static SCHEMA_UNUSED void table_block_default_free( void * context, void * pointer ) { (void) context; free( pointer ); } +static SCHEMA_UNUSED void * table_block_default_alloc( void * context, int64_t bytes ) { (void) context; return bytes>0 && (uint64_t)bytes<=SIZE_MAX ? schema_allocate(bytes) : NULL; } +static SCHEMA_UNUSED void table_block_default_free( void * context, void * pointer ) { (void) context; schema_release( pointer ); } static SCHEMA_UNUSED TableBlockAllocator table_block_default_allocator( void ) { @@ -129,6 +157,10 @@ static SCHEMA_UNUSED void * table_block_row_at( const TableBlockRows * rows, int return (void *) ( rows->base + (ptrdiff_t) index * rows->stride ); } +typedef struct TableBlockConstRows { const uint8_t * base; int32_t count, stride; } TableBlockConstRows; +static SCHEMA_UNUSED const void * table_block_row_at_const(const TableBlockConstRows * rows,int32_t index) +{ return rows->base+(ptrdiff_t)index*rows->stride; } + /* ---- reflection over a block (docs/SPEC-TABLES.md §8, §19.2) ---- The descriptors are the mechanism, and they are what retires a hand-kept @@ -209,6 +241,9 @@ static SCHEMA_UNUSED uint64_t table_block_read64( const uint8_t * p ) return v; } +static SCHEMA_UNUSED uint32_t table_block_read32(const uint8_t * p) +{ uint32_t value; memcpy(&value,p,4); return value; } + static SCHEMA_UNUSED int64_t table_block_align( int64_t offset, int64_t alignment ) { return ( offset + alignment - 1 ) / alignment * alignment; @@ -305,6 +340,8 @@ typedef struct TableEntityBlock int64_t bytes; /* the extent in use */ } TableEntityBlock; +typedef struct TableEntityBlockConst { const uint8_t * base; const TableEntityBlockProjection * projection; int64_t bytes; } TableEntityBlockConst; +static SCHEMA_UNUSED int64_t table_entity_block_bytes_const(const TableEntityBlockConst * block) { return block->bytes; } /* this table's block descriptors (docs/SPEC-TABLES.md §8, §19.2): constant data, defined in the .c beside this header. A consumer holding this reads the triples out of an instance and points at rows, with no hand-written @@ -406,6 +443,14 @@ static SCHEMA_UNUSED int table_entity_block_open( TableEntityBlock * block, void return schema_benchtable_table_entity_block_open_( block, base, bytes ); } +int schema_benchtable_table_entity_block_open_ex_(TableEntityBlock * block,void * base,int64_t bytes,TableRefuseReason * reason); +static SCHEMA_UNUSED int table_entity_block_open_ex(TableEntityBlock * block,void * base,int64_t bytes,TableRefuseReason * reason) +{ return schema_benchtable_table_entity_block_open_ex_(block,base,bytes,reason); } +int schema_benchtable_table_entity_block_open_const_ex_(TableEntityBlockConst * block,const void * base,int64_t bytes,TableRefuseReason * reason); +static SCHEMA_UNUSED int table_entity_block_open_const_ex(TableEntityBlockConst * block,const void * base,int64_t bytes,TableRefuseReason * reason) +{ return schema_benchtable_table_entity_block_open_const_ex_(block,base,bytes,reason); } +static SCHEMA_UNUSED int table_entity_block_open_const(TableEntityBlockConst * block,const void * base,int64_t bytes) +{ return schema_benchtable_table_entity_block_open_const_ex_(block,base,bytes,NULL); } /* ---- the block form of table TableEntity: end ---- */ /* ---- the block form of table TableStat (docs/SPEC-TABLES.md §19): begin ---- */ @@ -481,6 +526,8 @@ typedef struct TableStatBlock int64_t bytes; /* the extent in use */ } TableStatBlock; +typedef struct TableStatBlockConst { const uint8_t * base; const TableStatBlockProjection * projection; int64_t bytes; } TableStatBlockConst; +static SCHEMA_UNUSED int64_t table_stat_block_bytes_const(const TableStatBlockConst * block) { return block->bytes; } /* this table's block descriptors (docs/SPEC-TABLES.md §8, §19.2): constant data, defined in the .c beside this header. A consumer holding this reads the triples out of an instance and points at rows, with no hand-written @@ -570,6 +617,14 @@ static SCHEMA_UNUSED int table_stat_block_open( TableStatBlock * block, void * b return schema_benchtable_table_stat_block_open_( block, base, bytes ); } +int schema_benchtable_table_stat_block_open_ex_(TableStatBlock * block,void * base,int64_t bytes,TableRefuseReason * reason); +static SCHEMA_UNUSED int table_stat_block_open_ex(TableStatBlock * block,void * base,int64_t bytes,TableRefuseReason * reason) +{ return schema_benchtable_table_stat_block_open_ex_(block,base,bytes,reason); } +int schema_benchtable_table_stat_block_open_const_ex_(TableStatBlockConst * block,const void * base,int64_t bytes,TableRefuseReason * reason); +static SCHEMA_UNUSED int table_stat_block_open_const_ex(TableStatBlockConst * block,const void * base,int64_t bytes,TableRefuseReason * reason) +{ return schema_benchtable_table_stat_block_open_const_ex_(block,base,bytes,reason); } +static SCHEMA_UNUSED int table_stat_block_open_const(TableStatBlockConst * block,const void * base,int64_t bytes) +{ return schema_benchtable_table_stat_block_open_const_ex_(block,base,bytes,NULL); } /* ---- the block form of table TableStat: end ---- */ diff --git a/generated/bench/tables/c/BenchTableTable.c b/generated/bench/tables/c/BenchTableTable.c index 5d98419b7..f36d8f64f 100644 --- a/generated/bench/tables/c/BenchTableTable.c +++ b/generated/bench/tables/c/BenchTableTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else - { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,22 +2330,22 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableVariantInfo schema_benchtable_table_entity_weapon_variants_[] = { - { "None", 0x0000 }, - { "Fists", 0x03ed }, - { "Pistol", 0x54eb }, - { "Shotgun", 0xdf27 }, - { "Rifle", 0xaa64 }, - { "Sniper", 0x5513 }, - { "Smg", 0x138b }, - { "Rocket", 0x56fa }, - { "Grenade", 0xf3ed }, - { "Plasma", 0xa376 }, - { "Railgun", 0xeff8 }, - { "Flamer", 0xfa19 }, - { "Mine", 0x61bf }, - { "Turret", 0xf720 }, - { "Drone", 0xc67e }, - { "Repair", 0x86cc }, + { "None", 0x0000000000000000ull }, + { "Fists", 0xa790eee12766cf0cull }, + { "Pistol", 0x9fbfefa835da8476ull }, + { "Shotgun", 0x188bbb1783928a95ull }, + { "Rifle", 0x2c3667b9c2f272f1ull }, + { "Sniper", 0x0ca8b41b00d755f6ull }, + { "Smg", 0x985fc819fab21a4eull }, + { "Rocket", 0x229d0447c3086a55ull }, + { "Grenade", 0x011c7b49a7228f9dull }, + { "Plasma", 0x89f7566e1123e15full }, + { "Railgun", 0x8d7f25318b3b4469ull }, + { "Flamer", 0xd9cd0dcc285b7816ull }, + { "Mine", 0x04dc16aea8ff5276ull }, + { "Turret", 0x35e53bc341129217ull }, + { "Drone", 0x2cc8282fb0de8831ull }, + { "Repair", 0x20bada21bc8cb334ull }, }; static const TableVariantInfo schema_benchtable_table_entity_damage_variants_[] = { { "Bleeding", 0 }, @@ -1757,96 +2358,96 @@ static const TableVariantInfo schema_benchtable_table_entity_damage_variants_[] { "Downed", 0 }, }; static const TableFieldInfo schema_benchtable_table_entity_fields_[] = { - { "entity_id", "entity_id", "bits(12)", 0x5a13, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, entity_id ), (uint32_t) sizeof( ( (TableEntity *) 0 )->entity_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "pos_x", "pos_x", "int32", 0xaaa3, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, pos_x ), (uint32_t) sizeof( ( (TableEntity *) 0 )->pos_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "pos_y", "pos_y", "int32", 0xa5cc, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, pos_y ), (uint32_t) sizeof( ( (TableEntity *) 0 )->pos_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "pos_z", "pos_z", "int32", 0xa985, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, pos_z ), (uint32_t) sizeof( ( (TableEntity *) 0 )->pos_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "yaw", "yaw", "bits(9)", 0x80c1, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, yaw ), (uint32_t) sizeof( ( (TableEntity *) 0 )->yaw ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "pitch", "pitch", "bits(9)", 0xf783, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, pitch ), (uint32_t) sizeof( ( (TableEntity *) 0 )->pitch ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "vel_x", "vel_x", "int32", 0x03e2, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, vel_x ), (uint32_t) sizeof( ( (TableEntity *) 0 )->vel_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "vel_y", "vel_y", "int32", 0x0151, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, vel_y ), (uint32_t) sizeof( ( (TableEntity *) 0 )->vel_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "vel_z", "vel_z", "int32", 0x0e88, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, vel_z ), (uint32_t) sizeof( ( (TableEntity *) 0 )->vel_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "health", "health", "int32", 0x8617, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, health ), (uint32_t) sizeof( ( (TableEntity *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "weapon", "weapon", "TableWeapon", 0x4f72, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, weapon ), (uint32_t) sizeof( ( (TableEntity *) 0 )->weapon ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 15, schema_benchtable_table_entity_weapon_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "damage", "damage", "TableDamage", 0x15a9, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, damage ), (uint32_t) sizeof( ( (TableEntity *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 7, schema_benchtable_table_entity_damage_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "moving", "moving", "bool", 0xa4b2, 1, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, moving ), (uint32_t) sizeof( ( (TableEntity *) 0 )->moving ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "firing", "firing", "bool", 0x2302, 1, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, firing ), (uint32_t) sizeof( ( (TableEntity *) 0 )->firing ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "entity_id", "entity_id", "bits(12)", 0x23fcfd6678e36712ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, entity_id ), (uint32_t) sizeof( ( (TableEntity *) 0 )->entity_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pos_x", "pos_x", "int32", 0xcb4b37357667310eull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, pos_x ), (uint32_t) sizeof( ( (TableEntity *) 0 )->pos_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pos_y", "pos_y", "int32", 0xcb4b3835766732c1ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, pos_y ), (uint32_t) sizeof( ( (TableEntity *) 0 )->pos_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pos_z", "pos_z", "int32", 0xcb4b353576672da8ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, pos_z ), (uint32_t) sizeof( ( (TableEntity *) 0 )->pos_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -16383.0, 16383.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "yaw", "yaw", "bits(9)", 0xb54d8e19798e16e8ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, yaw ), (uint32_t) sizeof( ( (TableEntity *) 0 )->yaw ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pitch", "pitch", "bits(9)", 0x53a9f665a90cc1b1ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, pitch ), (uint32_t) sizeof( ( (TableEntity *) 0 )->pitch ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "vel_x", "vel_x", "int32", 0x6cede6b6eb60ee67ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, vel_x ), (uint32_t) sizeof( ( (TableEntity *) 0 )->vel_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "vel_y", "vel_y", "int32", 0x6cede5b6eb60ecb4ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, vel_y ), (uint32_t) sizeof( ( (TableEntity *) 0 )->vel_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "vel_z", "vel_z", "int32", 0x6cede8b6eb60f1cdull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, vel_z ), (uint32_t) sizeof( ( (TableEntity *) 0 )->vel_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -2048.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "health", "health", "int32", 0x7f69d4b5288ba9cfull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, health ), (uint32_t) sizeof( ( (TableEntity *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "weapon", "weapon", "TableWeapon", 0xa0b610205f2c6e01ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, weapon ), (uint32_t) sizeof( ( (TableEntity *) 0 )->weapon ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 15, schema_benchtable_table_entity_weapon_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "damage", "damage", "TableDamage", 0x7f6308be8ab37fc0ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, damage ), (uint32_t) sizeof( ( (TableEntity *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 7, schema_benchtable_table_entity_damage_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "moving", "moving", "bool", 0x11a44fc1d1243da7ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, moving ), (uint32_t) sizeof( ( (TableEntity *) 0 )->moving ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "firing", "firing", "bool", 0x7674cfd19b9031caull, 1, 0, 0, 0, 0, (uint32_t) offsetof( TableEntity, firing ), (uint32_t) sizeof( ( (TableEntity *) 0 )->firing ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_benchtable_table_entity_info_ = { "TableEntity", (uint32_t) sizeof( TableEntity ), 14, schema_benchtable_table_entity_fields_, schema_benchtable_table_entity_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_benchtable_table_stat_fields_[] = { - { "stat_id", "stat_id", "bits(8)", 0xfb6c, 6, 0, 0, 0, 0, (uint32_t) offsetof( TableStat, stat_id ), (uint32_t) sizeof( ( (TableStat *) 0 )->stat_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "delta", "delta", "int32", 0x1720, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableStat, delta ), (uint32_t) sizeof( ( (TableStat *) 0 )->delta ), 0xffffffffu, 0xffffffffu, NULL, 1, -512.0, 511.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "stat_id", "stat_id", "bits(8)", 0x80ab75f0866dbf65ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TableStat, stat_id ), (uint32_t) sizeof( ( (TableStat *) 0 )->stat_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "delta", "delta", "int32", 0x52076675ec13a0c1ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableStat, delta ), (uint32_t) sizeof( ( (TableStat *) 0 )->delta ), 0xffffffffu, 0xffffffffu, NULL, 1, -512.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_benchtable_table_stat_info_ = { "TableStat", (uint32_t) sizeof( TableStat ), 2, schema_benchtable_table_stat_fields_, schema_benchtable_table_stat_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_benchtable_table_mixed_game_event_variants_[] = { { "None", 0 }, - { "hit", 0xba78 }, - { "chat", 0x5be0 }, - { "pickup", 0x99dd }, + { "hit", 0x33732819300680aaull }, + { "chat", 0xf2a38d910b5b348bull }, + { "pickup", 0x9fa3a41c86ecb765ull }, }; static const TableUnionArmInfo schema_benchtable_table_mixed_game_event_arms_[] = { - { 0, NULL }, - { (uint32_t) offsetof( TableEvent, as.hit ), &schema_benchtable_table_hit_event_info_ }, - { (uint32_t) offsetof( TableEvent, as.chat ), &schema_benchtable_table_chat_event_info_ }, - { (uint32_t) offsetof( TableEvent, as.pickup ), &schema_benchtable_table_pickup_event_info_ }, + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( TableEvent, as.hit ), &schema_benchtable_table_hit_event_info_, NULL, (uint32_t)sizeof( ((TableEvent *)0)->as.hit ) }, + { (uint32_t)offsetof( TableEvent, as.chat ), &schema_benchtable_table_chat_event_info_, NULL, (uint32_t)sizeof( ((TableEvent *)0)->as.chat ) }, + { (uint32_t)offsetof( TableEvent, as.pickup ), &schema_benchtable_table_pickup_event_info_, NULL, (uint32_t)sizeof( ((TableEvent *)0)->as.pickup ) }, }; static const TableUnionInfo schema_benchtable_table_mixed_game_event_union_ = { (uint32_t) offsetof( TableEvent, type ), (uint32_t) sizeof( ( (TableEvent *) 0 )->type ), schema_benchtable_table_mixed_game_event_arms_ }; static const TableFieldInfo schema_benchtable_table_mixed_fields_[] = { - { "protocol_magic", "protocol_magic", "uint16", 0xae30, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, protocol_magic ), (uint32_t) sizeof( ( (TableMixed *) 0 )->protocol_magic ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "sequence", "sequence", "bits(16)", 0xd32b, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, sequence ), (uint32_t) sizeof( ( (TableMixed *) 0 )->sequence ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "ack_sequence", "ack_sequence", "int32", 0x3363, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, ack_sequence ), (uint32_t) sizeof( ( (TableMixed *) 0 )->ack_sequence ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "ack_bits", "ack_bits", "bits(32)", 0xebb9, 8, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, ack_bits ), (uint32_t) sizeof( ( (TableMixed *) 0 )->ack_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "session_id", "session_id", "uint64", 0x8790, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, session_id ), (uint32_t) sizeof( ( (TableMixed *) 0 )->session_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "client_id", "client_id", "uint32", 0xd443, 8, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, client_id ), (uint32_t) sizeof( ( (TableMixed *) 0 )->client_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "nonce", "nonce", "uint64", 0x80f0, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, nonce ), (uint32_t) sizeof( ( (TableMixed *) 0 )->nonce ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 9.223372036854776e+18, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "world_time", "world_time", "int64", 0x77f2, 5, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, world_time ), (uint32_t) sizeof( ( (TableMixed *) 0 )->world_time ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+12, 1e+12, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "frame_tick", "frame_tick", "bits(48)", 0xcbc2, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, frame_tick ), (uint32_t) sizeof( ( (TableMixed *) 0 )->frame_tick ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "server_time", "server_time", "float32", 0x27f9, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, server_time ), (uint32_t) sizeof( ( (TableMixed *) 0 )->server_time ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "entities", "entities", "TableEntity", 0x25e3, 13, 1, 1, 0, 8, (uint32_t) offsetof( TableMixed, entities ), (uint32_t) sizeof( ( (TableMixed *) 0 )->entities[0] ), (uint32_t) offsetof( TableMixed, entities_count ), 0xffffffffu, &schema_benchtable_table_entity_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "stats", "stats", "TableStat", 0x76dd, 13, 1, 1, 0, 80, (uint32_t) offsetof( TableMixed, stats ), (uint32_t) sizeof( ( (TableMixed *) 0 )->stats[0] ), (uint32_t) offsetof( TableMixed, stats_count ), 0xffffffffu, &schema_benchtable_table_stat_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "game_event", "game_event", "TableEvent", 0xa17e, 15, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, game_event ), (uint32_t) sizeof( ( (TableMixed *) 0 )->game_event ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_benchtable_table_mixed_game_event_variants_, 1, NULL, NULL, -1, &schema_benchtable_table_mixed_game_event_union_, "", TableDocNone, 0, NULL }, - { "loadout", "loadout", "uint8", 0x9f78, 6, 1, 0, 0, 4, (uint32_t) offsetof( TableMixed, loadout ), (uint32_t) sizeof( ( (TableMixed *) 0 )->loadout[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "player_name", "player_name", "string", 0x2d3e, 12, 0, 1, 0, 15, (uint32_t) offsetof( TableMixed, player_name ), (uint32_t) sizeof( ( (TableMixed *) 0 )->player_name ), (uint32_t) offsetof( TableMixed, player_name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "payload", "payload", "bytes", 0x44aa, 6, 1, 1, 0, 16, (uint32_t) offsetof( TableMixed, payload ), (uint32_t) sizeof( ( (TableMixed *) 0 )->payload[0] ), (uint32_t) offsetof( TableMixed, payload_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "aim_x", "aim_x", "float32", 0x84e9, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, aim_x ), (uint32_t) sizeof( ( (TableMixed *) 0 )->aim_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "aim_y", "aim_y", "float32", 0x8d96, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, aim_y ), (uint32_t) sizeof( ( (TableMixed *) 0 )->aim_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "aim_z", "aim_z", "float32", 0x8e03, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, aim_z ), (uint32_t) sizeof( ( (TableMixed *) 0 )->aim_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "recoil", "recoil", "float32", 0x2d04, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, recoil ), (uint32_t) sizeof( ( (TableMixed *) 0 )->recoil ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "drift", "drift", "float64", 0xc023, 11, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, drift ), (uint32_t) sizeof( ( (TableMixed *) 0 )->drift ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "wide_key", "wide_key", "uint64", 0x272f, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, wide_key ), (uint32_t) sizeof( ( (TableMixed *) 0 )->wide_key ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "flux", "flux", "int64", 0x196a, 5, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, flux ), (uint32_t) sizeof( ( (TableMixed *) 0 )->flux ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+18, 1e+18, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "ping", "ping", "float32", 0xe6d4, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, ping ), (uint32_t) sizeof( ( (TableMixed *) 0 )->ping ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 250.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "crc_hint", "crc_hint", "bits(24)", 0xd3dc, 8, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, crc_hint ), (uint32_t) sizeof( ( (TableMixed *) 0 )->crc_hint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.6777215e+07, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "has_extra", "has_extra", "bool", 0xb023, 1, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, has_extra ), (uint32_t) sizeof( ( (TableMixed *) 0 )->has_extra ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "extra", "extra", "int32", 0xb579, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, extra ), (uint32_t) sizeof( ( (TableMixed *) 0 )->extra ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, -1, NULL, 0, NULL, NULL, -1, NULL, "has_extra", TableDocNone, 0, NULL }, - { "idle_ticks", "idle_ticks", "int32", 0x9555, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, idle_ticks ), (uint32_t) sizeof( ( (TableMixed *) 0 )->idle_ticks ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 15.0, -1, NULL, 0, NULL, NULL, -1, NULL, "!has_extra", TableDocNone, 0, NULL }, + { "protocol_magic", "protocol_magic", "uint16", 0x6a5a70d91aa115fdull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, protocol_magic ), (uint32_t) sizeof( ( (TableMixed *) 0 )->protocol_magic ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "sequence", "sequence", "bits(16)", 0xaa38aca481f528a8ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, sequence ), (uint32_t) sizeof( ( (TableMixed *) 0 )->sequence ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ack_sequence", "ack_sequence", "int32", 0x0dbe005c56697c3eull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, ack_sequence ), (uint32_t) sizeof( ( (TableMixed *) 0 )->ack_sequence ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ack_bits", "ack_bits", "bits(32)", 0x9bae0da8b829ee03ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, ack_bits ), (uint32_t) sizeof( ( (TableMixed *) 0 )->ack_bits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "session_id", "session_id", "uint64", 0xb7d7b5650a590b05ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, session_id ), (uint32_t) sizeof( ( (TableMixed *) 0 )->session_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "client_id", "client_id", "uint32", 0x6d7b98e2d095967eull, 8, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, client_id ), (uint32_t) sizeof( ( (TableMixed *) 0 )->client_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "nonce", "nonce", "uint64", 0x73a94c71d60dc0d8ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, nonce ), (uint32_t) sizeof( ( (TableMixed *) 0 )->nonce ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 9.223372036854776e+18, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "world_time", "world_time", "int64", 0x3eee6b51be54fc85ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, world_time ), (uint32_t) sizeof( ( (TableMixed *) 0 )->world_time ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+12, 1e+12, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "frame_tick", "frame_tick", "bits(48)", 0x7bbc035f7b6d0112ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, frame_tick ), (uint32_t) sizeof( ( (TableMixed *) 0 )->frame_tick ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "server_time", "server_time", "float32", 0x3c460475f9be69c6ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, server_time ), (uint32_t) sizeof( ( (TableMixed *) 0 )->server_time ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "entities", "entities", "TableEntity", 0x935d0fb07bb3822aull, 13, 1, 1, 0, 8, (uint32_t) offsetof( TableMixed, entities ), (uint32_t) sizeof( ( (TableMixed *) 0 )->entities[0] ), (uint32_t) offsetof( TableMixed, entities_count ), 0xffffffffu, &schema_benchtable_table_entity_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "stats", "stats", "TableStat", 0xee639cad45b1994cull, 13, 1, 1, 0, 80, (uint32_t) offsetof( TableMixed, stats ), (uint32_t) sizeof( ( (TableMixed *) 0 )->stats[0] ), (uint32_t) offsetof( TableMixed, stats_count ), 0xffffffffu, &schema_benchtable_table_stat_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "game_event", "game_event", "TableEvent", 0x2e35dc5321aa5790ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, game_event ), (uint32_t) sizeof( ( (TableMixed *) 0 )->game_event ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_benchtable_table_mixed_game_event_variants_, 1, NULL, NULL, -1, &schema_benchtable_table_mixed_game_event_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "loadout", "loadout", "uint8", 0x5759ce7586bbb5a3ull, 6, 1, 0, 0, 4, (uint32_t) offsetof( TableMixed, loadout ), (uint32_t) sizeof( ( (TableMixed *) 0 )->loadout[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "player_name", "player_name", "string", 0x13a62f542cf8e86eull, 12, 0, 1, 0, 15, (uint32_t) offsetof( TableMixed, player_name ), (uint32_t) sizeof( ( (TableMixed *) 0 )->player_name ), (uint32_t) offsetof( TableMixed, player_name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "payload", "payload", "bytes", 0xcfb8a9d063b5e9e5ull, 6, 1, 1, 0, 16, (uint32_t) offsetof( TableMixed, payload ), (uint32_t) sizeof( ( (TableMixed *) 0 )->payload[0] ), (uint32_t) offsetof( TableMixed, payload_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "aim_x", "aim_x", "float32", 0xdbaf7be5e24296c9ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, aim_x ), (uint32_t) sizeof( ( (TableMixed *) 0 )->aim_x ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "aim_y", "aim_y", "float32", 0xdbaf7ae5e2429516ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, aim_y ), (uint32_t) sizeof( ( (TableMixed *) 0 )->aim_y ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "aim_z", "aim_z", "float32", 0xdbaf79e5e2429363ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, aim_z ), (uint32_t) sizeof( ( (TableMixed *) 0 )->aim_z ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "recoil", "recoil", "float32", 0x9cef31fff7e2e457ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, recoil ), (uint32_t) sizeof( ( (TableMixed *) 0 )->recoil ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "drift", "drift", "float64", 0x5ab3f9c9341f6c04ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, drift ), (uint32_t) sizeof( ( (TableMixed *) 0 )->drift ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "wide_key", "wide_key", "uint64", 0xa3348580461faf16ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, wide_key ), (uint32_t) sizeof( ( (TableMixed *) 0 )->wide_key ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flux", "flux", "int64", 0xd61bdd7908af2642ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, flux ), (uint32_t) sizeof( ( (TableMixed *) 0 )->flux ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+18, 1e+18, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ping", "ping", "float32", 0xbf30e00dc53307a9ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, ping ), (uint32_t) sizeof( ( (TableMixed *) 0 )->ping ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 250.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "crc_hint", "crc_hint", "bits(24)", 0x560d6527ccd8515full, 8, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, crc_hint ), (uint32_t) sizeof( ( (TableMixed *) 0 )->crc_hint ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.6777215e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_extra", "has_extra", "bool", 0xc08292176cfd8672ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, has_extra ), (uint32_t) sizeof( ( (TableMixed *) 0 )->has_extra ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "extra", "extra", "int32", 0xfd29ee12a979cb69ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, extra ), (uint32_t) sizeof( ( (TableMixed *) 0 )->extra ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "has_extra", TableDocNone, 0, NULL, 0, NULL }, + { "idle_ticks", "idle_ticks", "int32", 0x78101ac0aa8cbcfeull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableMixed, idle_ticks ), (uint32_t) sizeof( ( (TableMixed *) 0 )->idle_ticks ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 15.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!has_extra", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_benchtable_table_mixed_info_ = { "TableMixed", (uint32_t) sizeof( TableMixed ), 28, schema_benchtable_table_mixed_fields_, schema_benchtable_table_mixed_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_benchtable_table_hit_event_fields_[] = { - { "target_id", "target_id", "bits(12)", 0xdf6a, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableHitEvent, target_id ), (uint32_t) sizeof( ( (TableHitEvent *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "damage", "damage", "int32", 0x15a9, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableHitEvent, damage ), (uint32_t) sizeof( ( (TableHitEvent *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "hit_kind", "hit_kind", "int32", 0xaf83, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableHitEvent, hit_kind ), (uint32_t) sizeof( ( (TableHitEvent *) 0 )->hit_kind ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "crit", "crit", "bool", 0x93d9, 1, 0, 0, 0, 0, (uint32_t) offsetof( TableHitEvent, crit ), (uint32_t) sizeof( ( (TableHitEvent *) 0 )->crit ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "target_id", "target_id", "bits(12)", 0xb7bc9ac015a25050ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableHitEvent, target_id ), (uint32_t) sizeof( ( (TableHitEvent *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "damage", "damage", "int32", 0x7f6308be8ab37fc0ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableHitEvent, damage ), (uint32_t) sizeof( ( (TableHitEvent *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "hit_kind", "hit_kind", "int32", 0x01fbc365b059b925ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableHitEvent, hit_kind ), (uint32_t) sizeof( ( (TableHitEvent *) 0 )->hit_kind ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "crit", "crit", "bool", 0x126167908c9aa52dull, 1, 0, 0, 0, 0, (uint32_t) offsetof( TableHitEvent, crit ), (uint32_t) sizeof( ( (TableHitEvent *) 0 )->crit ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_benchtable_table_hit_event_info_ = { "TableHitEvent", (uint32_t) sizeof( TableHitEvent ), 4, schema_benchtable_table_hit_event_fields_, schema_benchtable_table_hit_event_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_benchtable_table_chat_event_fields_[] = { - { "channel", "channel", "int32", 0x7366, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableChatEvent, channel ), (uint32_t) sizeof( ( (TableChatEvent *) 0 )->channel ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "speaker", "speaker", "bits(12)", 0xce0b, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableChatEvent, speaker ), (uint32_t) sizeof( ( (TableChatEvent *) 0 )->speaker ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "channel", "channel", "int32", 0xa5013e9ad5caeda4ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TableChatEvent, channel ), (uint32_t) sizeof( ( (TableChatEvent *) 0 )->channel ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "speaker", "speaker", "bits(12)", 0xfbf1ac4d96ebd022ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TableChatEvent, speaker ), (uint32_t) sizeof( ( (TableChatEvent *) 0 )->speaker ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_benchtable_table_chat_event_info_ = { "TableChatEvent", (uint32_t) sizeof( TableChatEvent ), 2, schema_benchtable_table_chat_event_fields_, schema_benchtable_table_chat_event_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_benchtable_table_pickup_event_fields_[] = { - { "item_id", "item_id", "bits(10)", 0xec67, 7, 0, 0, 0, 0, (uint32_t) offsetof( TablePickupEvent, item_id ), (uint32_t) sizeof( ( (TablePickupEvent *) 0 )->item_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1023.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "amount", "amount", "int32", 0x39cc, 4, 0, 0, 0, 0, (uint32_t) offsetof( TablePickupEvent, amount ), (uint32_t) sizeof( ( (TablePickupEvent *) 0 )->amount ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "item_id", "item_id", "bits(10)", 0x9e7fd06d864fbd56ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TablePickupEvent, item_id ), (uint32_t) sizeof( ( (TablePickupEvent *) 0 )->item_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1023.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "amount", "amount", "int32", 0x8113fe7ea2b16969ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TablePickupEvent, amount ), (uint32_t) sizeof( ( (TablePickupEvent *) 0 )->amount ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_benchtable_table_pickup_event_info_ = { "TablePickupEvent", (uint32_t) sizeof( TablePickupEvent ), 2, schema_benchtable_table_pickup_event_fields_, schema_benchtable_table_pickup_event_reset_raw_, TableDocNone, 0, NULL }; diff --git a/generated/bench/tables/c/BenchTableTable.h b/generated/bench/tables/c/BenchTableTable.h index 9dc7561c2..ed8235dc7 100644 --- a/generated/bench/tables/c/BenchTableTable.h +++ b/generated/bench/tables/c/BenchTableTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package benchtable — protocol id 0x0926221bcb6f475f (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_BENCHTABLE_BENCHTABLETABLE_H #define SCHEMA_BENCHTABLE_BENCHTABLETABLE_H @@ -107,8 +107,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -117,6 +123,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -126,6 +133,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -152,7 +161,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -162,12 +171,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -181,6 +192,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -208,6 +221,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -229,137 +244,252 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[79]; + uint32_t slots[256], positions[79]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (256-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(256-1); + } + if(v->count==79) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; - } - return 0; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -369,6 +499,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_BENCHTABLE_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_BENCHTABLE_BUILD_VERSION #define SCHEMA_BENCHTABLE_BUILD_VERSION @@ -480,25 +665,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -508,40 +693,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_BENCHTABLE_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table TableEntity — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in table_entity_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -732,8 +1498,8 @@ static SCHEMA_UNUSED int64_t table_stat_measure( const TableStat * value ); static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_stat_save_body( TableWriter * w, const TableStat * value ); static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_stat_load_body( TableReader * r, TableStat * value ); static SCHEMA_UNUSED int64_t table_mixed_measure( const TableMixed * value ); -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_save_body( TableWriter * w, const TableMixed * value ); -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( TableReader * r, TableMixed * value ); +static SCHEMA_UNUSED int table_mixed_save_body( TableWriter * w, const TableMixed * value ); +static SCHEMA_UNUSED int table_mixed_load_body( TableReader * r, TableMixed * value ); static SCHEMA_UNUSED int64_t table_hit_event_measure( const TableHitEvent * value ); static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_hit_event_save_body( TableWriter * w, const TableHitEvent * value ); static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_hit_event_load_body( TableReader * r, TableHitEvent * value ); @@ -744,386 +1510,1272 @@ static SCHEMA_UNUSED int64_t table_pickup_event_measure( const TablePickupEvent static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_pickup_event_save_body( TableWriter * w, const TablePickupEvent * value ); static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_pickup_event_load_body( TableReader * r, TablePickupEvent * value ); -static SCHEMA_UNUSED int64_t table_entity_measure( const TableEntity * value ) +static SCHEMA_UNUSED int schema_benchtable_table_entity_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_benchtable_table_stat_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_benchtable_table_mixed_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_benchtable_table_hit_event_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_benchtable_table_chat_event_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_benchtable_table_pickup_event_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int table_entity_save_message_body(TableBitWriter *,const TableEntity *); +static SCHEMA_UNUSED int table_stat_save_message_body(TableBitWriter *,const TableStat *); +static SCHEMA_UNUSED int table_mixed_save_message_body(TableBitWriter *,const TableMixed *); +static SCHEMA_UNUSED int table_hit_event_save_message_body(TableBitWriter *,const TableHitEvent *); +static SCHEMA_UNUSED int table_chat_event_save_message_body(TableBitWriter *,const TableChatEvent *); +static SCHEMA_UNUSED int table_pickup_event_save_message_body(TableBitWriter *,const TablePickupEvent *); +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_message_save_(TableBitWriter *,const TableEvent *); +static SCHEMA_UNUSED int table_entity_load_message_body(TableMessageReader *,TableEntity *); +static SCHEMA_UNUSED int table_stat_load_message_body(TableMessageReader *,TableStat *); +static SCHEMA_UNUSED int table_mixed_load_message_body(TableMessageReader *,TableMixed *); +static SCHEMA_UNUSED int table_hit_event_load_message_body(TableMessageReader *,TableHitEvent *); +static SCHEMA_UNUSED int table_chat_event_load_message_body(TableMessageReader *,TableChatEvent *); +static SCHEMA_UNUSED int table_pickup_event_load_message_body(TableMessageReader *,TablePickupEvent *); +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_message_load_(TableMessageReader *,TableEvent *); +static SCHEMA_UNUSED int schema_benchtable_table_entity_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_benchtable_table_stat_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_benchtable_table_mixed_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_benchtable_table_hit_event_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_benchtable_table_chat_event_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_benchtable_table_pickup_event_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_benchtable_table_event_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_message_save_(TableBitWriter * w,const TableEvent * value) { - int64_t bytes = 2; /* terminator */ - if ( value->entity_id != 0 ) { bytes += 3 + 2; } /* entity_id */ - if ( value->pos_x != 0 ) { bytes += 3 + 4; } /* pos_x */ - if ( value->pos_y != 0 ) { bytes += 3 + 4; } /* pos_y */ - if ( value->pos_z != 0 ) { bytes += 3 + 4; } /* pos_z */ - if ( value->yaw != 0 ) { bytes += 3 + 2; } /* yaw */ - if ( value->pitch != 0 ) { bytes += 3 + 2; } /* pitch */ - if ( value->vel_x != 0 ) { bytes += 3 + 4; } /* vel_x */ - if ( value->vel_y != 0 ) { bytes += 3 + 4; } /* vel_y */ - if ( value->vel_z != 0 ) { bytes += 3 + 4; } /* vel_z */ - if ( value->health != 0 ) { bytes += 3 + 4; } /* health */ - if ( value->weapon != TABLE_WEAPON_NONE ) - { - uint16_t id_weapon = 0; - switch ( value->weapon ) /* TableWeapon rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TABLE_WEAPON_NONE: id_weapon = 0; break; - case TABLE_WEAPON_FISTS: id_weapon = 0x03ed; break; - case TABLE_WEAPON_PISTOL: id_weapon = 0x54eb; break; - case TABLE_WEAPON_SHOTGUN: id_weapon = 0xdf27; break; - case TABLE_WEAPON_RIFLE: id_weapon = 0xaa64; break; - case TABLE_WEAPON_SNIPER: id_weapon = 0x5513; break; - case TABLE_WEAPON_SMG: id_weapon = 0x138b; break; - case TABLE_WEAPON_ROCKET: id_weapon = 0x56fa; break; - case TABLE_WEAPON_GRENADE: id_weapon = 0xf3ed; break; - case TABLE_WEAPON_PLASMA: id_weapon = 0xa376; break; - case TABLE_WEAPON_RAILGUN: id_weapon = 0xeff8; break; - case TABLE_WEAPON_FLAMER: id_weapon = 0xfa19; break; - case TABLE_WEAPON_MINE: id_weapon = 0x61bf; break; - case TABLE_WEAPON_TURRET: id_weapon = 0xf720; break; - case TABLE_WEAPON_DRONE: id_weapon = 0xc67e; break; - case TABLE_WEAPON_REPAIR: id_weapon = 0x86cc; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) id_weapon; - bytes += 3 + 2; /* weapon: the variant's name hash */ - } - if ( value->damage != 0 ) { bytes += 3 + 8; } /* damage */ - if ( value->moving != 0 ) { bytes += 3 + 1; } /* moving */ - if ( value->firing != 0 ) { bytes += 3 + 1; } /* firing */ - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_entity_save_body( TableWriter * w, const TableEntity * value ) -{ - if ( value->entity_id != 0 ) - { - table_writer_put16( w, 0x5a13 ); table_writer_put8( w, 7 ); /* entity_id */ - table_writer_put16( w, (uint16_t) ( value->entity_id ) ); - } - if ( value->pos_x != 0 ) - { - table_writer_put16( w, 0xaaa3 ); table_writer_put8( w, 4 ); /* pos_x */ - table_writer_put32( w, (uint32_t) ( value->pos_x ) ); - } - if ( value->pos_y != 0 ) - { - table_writer_put16( w, 0xa5cc ); table_writer_put8( w, 4 ); /* pos_y */ - table_writer_put32( w, (uint32_t) ( value->pos_y ) ); - } - if ( value->pos_z != 0 ) - { - table_writer_put16( w, 0xa985 ); table_writer_put8( w, 4 ); /* pos_z */ - table_writer_put32( w, (uint32_t) ( value->pos_z ) ); - } - if ( value->yaw != 0 ) - { - table_writer_put16( w, 0x80c1 ); table_writer_put8( w, 7 ); /* yaw */ - table_writer_put16( w, (uint16_t) ( value->yaw ) ); - } - if ( value->pitch != 0 ) - { - table_writer_put16( w, 0xf783 ); table_writer_put8( w, 7 ); /* pitch */ - table_writer_put16( w, (uint16_t) ( value->pitch ) ); - } - if ( value->vel_x != 0 ) - { - table_writer_put16( w, 0x03e2 ); table_writer_put8( w, 4 ); /* vel_x */ - table_writer_put32( w, (uint32_t) ( value->vel_x ) ); - } - if ( value->vel_y != 0 ) - { - table_writer_put16( w, 0x0151 ); table_writer_put8( w, 4 ); /* vel_y */ - table_writer_put32( w, (uint32_t) ( value->vel_y ) ); - } - if ( value->vel_z != 0 ) - { - table_writer_put16( w, 0x0e88 ); table_writer_put8( w, 4 ); /* vel_z */ - table_writer_put32( w, (uint32_t) ( value->vel_z ) ); - } - if ( value->health != 0 ) - { - table_writer_put16( w, 0x8617 ); table_writer_put8( w, 4 ); /* health */ - table_writer_put32( w, (uint32_t) ( value->health ) ); - } - if ( value->weapon != TABLE_WEAPON_NONE ) - { - uint16_t id_weapon = 0; - switch ( value->weapon ) /* TableWeapon rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TABLE_WEAPON_NONE: id_weapon = 0; break; - case TABLE_WEAPON_FISTS: id_weapon = 0x03ed; break; - case TABLE_WEAPON_PISTOL: id_weapon = 0x54eb; break; - case TABLE_WEAPON_SHOTGUN: id_weapon = 0xdf27; break; - case TABLE_WEAPON_RIFLE: id_weapon = 0xaa64; break; - case TABLE_WEAPON_SNIPER: id_weapon = 0x5513; break; - case TABLE_WEAPON_SMG: id_weapon = 0x138b; break; - case TABLE_WEAPON_ROCKET: id_weapon = 0x56fa; break; - case TABLE_WEAPON_GRENADE: id_weapon = 0xf3ed; break; - case TABLE_WEAPON_PLASMA: id_weapon = 0xa376; break; - case TABLE_WEAPON_RAILGUN: id_weapon = 0xeff8; break; - case TABLE_WEAPON_FLAMER: id_weapon = 0xfa19; break; - case TABLE_WEAPON_MINE: id_weapon = 0x61bf; break; - case TABLE_WEAPON_TURRET: id_weapon = 0xf720; break; - case TABLE_WEAPON_DRONE: id_weapon = 0xc67e; break; - case TABLE_WEAPON_REPAIR: id_weapon = 0x86cc; break; - default: return 0; /* no variant names this value: no wire identity */ - } - table_writer_put16( w, 0x4f72 ); table_writer_put8( w, 7 ); /* weapon */ - table_writer_put16( w, id_weapon ); - } - if ( value->damage != 0 ) - { - table_writer_put16( w, 0x15a9 ); table_writer_put8( w, 9 ); /* damage */ - table_writer_put64( w, (uint64_t) ( value->damage ) ); - } - if ( value->moving != 0 ) - { - table_writer_put16( w, 0xa4b2 ); table_writer_put8( w, 1 ); /* moving */ - table_writer_put8( w, value->moving ? 1 : 0 ); - } - if ( value->firing != 0 ) - { - table_writer_put16( w, 0x2302 ); table_writer_put8( w, 1 ); /* firing */ - table_writer_put8( w, value->firing ? 1 : 0 ); - } - table_writer_put16( w, 0 ); /* terminator */ + switch(value->type) { + case TABLE_EVENT_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TABLE_EVENT_TYPE_HIT: { + table_bit_put(w,68,kTableMessageRefBitsHere); + if(!table_hit_event_save_message_body(w,&(*value).as.hit)) return 0; + break; + } + case TABLE_EVENT_TYPE_CHAT: { + table_bit_put(w,69,kTableMessageRefBitsHere); + if(!table_chat_event_save_message_body(w,&(*value).as.chat)) return 0; + break; + } + case TABLE_EVENT_TYPE_PICKUP: { + table_bit_put(w,70,kTableMessageRefBitsHere); + if(!table_pickup_event_save_message_body(w,&(*value).as.pickup)) return 0; + break; + } + default: return 0; + } return !w->overflow; +} +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_message_load_(TableMessageReader * r,TableEvent * value) +{ + const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + value->type=0; + if(entry==NULL)return 1; + if(entry->kind==0)goto malformed; + switch(entry->id){ + case UINT64_C(0x33732819300680aa): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + memset(&(*value).as.hit,0,sizeof((*value).as.hit)); + if(!table_hit_event_load_message_body(r,&(*value).as.hit))return 0; + value->type=TABLE_EVENT_TYPE_HIT; + break; + } + case UINT64_C(0xf2a38d910b5b348b): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + memset(&(*value).as.chat,0,sizeof((*value).as.chat)); + if(!table_chat_event_load_message_body(r,&(*value).as.chat))return 0; + value->type=TABLE_EVENT_TYPE_CHAT; + break; + } + case UINT64_C(0x9fa3a41c86ecb765): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + memset(&(*value).as.pickup,0,sizeof((*value).as.pickup)); + if(!table_pickup_event_load_message_body(r,&(*value).as.pickup))return 0; + value->type=TABLE_EVENT_TYPE_PICKUP; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + }return 1; + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_benchtable_table_event_message_extent_(TableMessageReader * r,int64_t * at) +{ + const TableMessageEntry * entry; + (void)at; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(entry->kind==0)return 0; + switch(entry->id){ + default:break; + }return table_message_skip(r,entry); +} +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_save_( TableWriter * w, const TableEvent * value ); +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_load_( TableReader * r, TableEvent * value, int element ); +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_arm_hit_( TableWriter * w, const TableEvent * value ) +{ + if ( !table_hit_event_save_body( w, &(*value).as.hit ) ) { return 0; } return !w->overflow; } -static SCHEMA_UNUSED int64_t table_entity_save( const TableEntity * value, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_arm_chat_( TableWriter * w, const TableEvent * value ) { - TableWriter w = table_writer_make( buffer, capacity ); - if ( !table_entity_save_body( &w, value ) ) { return -1; } - return w.offset; /* == table_entity_measure( value ) */ + if ( !table_chat_event_save_body( w, &(*value).as.chat ) ) { return 0; } + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_entity_load_body( TableReader * r, TableEntity * value ) +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_arm_pickup_( TableWriter * w, const TableEvent * value ) { - table_entity_reset( value ); /* prefill declared defaults in place, then overlay */ - for ( ;; ) + if ( !table_pickup_event_save_body( w, &(*value).as.pickup ) ) { return 0; } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_save_( TableWriter * w, const TableEvent * value ) +{ + switch ( value->type ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) - { - case 0x5a13: /* entity_id */ + case TABLE_EVENT_TYPE_NONE: table_writer_leb( w, 0 ); break; + case TABLE_EVENT_TYPE_HIT: + table_writer_id( w, 0x33732819300680aaull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) { - if ( kind != 7 ) - { - r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } - break; - } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } - { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ - value->entity_id = decoded_v; - } - break; + int64_t frame_begin = w->offset; + if ( !schema_benchtable_table_event_wire_arm_hit_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - case 0xaaa3: /* pos_x */ + else { - if ( kind != 4 ) - { - r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } - break; - } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } - else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } - value->pos_x = decoded_v; - } - break; + TableWriter probe = table_writer_probe( w ); + if ( !schema_benchtable_table_event_wire_arm_hit_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_benchtable_table_event_wire_arm_hit_( w, value ) ) { return 0; } } - case 0xa5cc: /* pos_y */ + break; + case TABLE_EVENT_TYPE_CHAT: + table_writer_id( w, 0xf2a38d910b5b348bull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) { - if ( kind != 4 ) - { - r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } - break; - } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } - else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } - value->pos_y = decoded_v; - } - break; + int64_t frame_begin = w->offset; + if ( !schema_benchtable_table_event_wire_arm_chat_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - case 0xa985: /* pos_z */ + else { - if ( kind != 4 ) - { - r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } - break; - } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } - else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } - value->pos_z = decoded_v; - } - break; + TableWriter probe = table_writer_probe( w ); + if ( !schema_benchtable_table_event_wire_arm_chat_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_benchtable_table_event_wire_arm_chat_( w, value ) ) { return 0; } } - case 0x80c1: /* yaw */ + break; + case TABLE_EVENT_TYPE_PICKUP: + table_writer_id( w, 0x9fa3a41c86ecb765ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) { - if ( kind != 7 ) - { - r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } - break; - } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } - { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 511ull ) { decoded_v = 511ull; r->report->clamped++; } /* bits(9) width clamp */ - value->yaw = decoded_v; - } - break; + int64_t frame_begin = w->offset; + if ( !schema_benchtable_table_event_wire_arm_pickup_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - case 0xf783: /* pitch */ + else { - if ( kind != 7 ) - { + TableWriter probe = table_writer_probe( w ); + if ( !schema_benchtable_table_event_wire_arm_pickup_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_benchtable_table_event_wire_arm_pickup_( w, value ) ) { return 0; } + } + break; + default: return 0; + } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_benchtable_table_event_wire_load_( TableReader * r, TableEvent * value, int element ) +{ + uint64_t ref; uint8_t kind; TableReader arm; + if ( !table_reader_leb( r, &ref ) || ref > r->id_count ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { value->type = 0; return 1; } + if ( element ) { value->type = 0; } + if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + kind = table_reader_get8( r ); + if ( !table_reader_span( r, &arm ) ) { r->report->malformed = 1; return 0; } + value->type = 0; + switch ( table_reader_id_at( r, ref ) ) + { + case 0x33732819300680aaull: + { + if ( kind != 13 && !table_kind_widens( kind, 13 ) ) { r->report->kind_mismatch++; break; } + table_hit_event_load_body( &arm, &(*value).as.hit ); + if ( arm.offset != arm.size ) { r->report->malformed = 1; break; } + if ( kind != 13 ) { r->report->widened++; } + value->type = TABLE_EVENT_TYPE_HIT; + break; + } + case 0xf2a38d910b5b348bull: + { + if ( kind != 13 && !table_kind_widens( kind, 13 ) ) { r->report->kind_mismatch++; break; } + table_chat_event_load_body( &arm, &(*value).as.chat ); + if ( arm.offset != arm.size ) { r->report->malformed = 1; break; } + if ( kind != 13 ) { r->report->widened++; } + value->type = TABLE_EVENT_TYPE_CHAT; + break; + } + case 0x9fa3a41c86ecb765ull: + { + if ( kind != 13 && !table_kind_widens( kind, 13 ) ) { r->report->kind_mismatch++; break; } + table_pickup_event_load_body( &arm, &(*value).as.pickup ); + if ( arm.offset != arm.size ) { r->report->malformed = 1; break; } + if ( kind != 13 ) { r->report->widened++; } + value->type = TABLE_EVENT_TYPE_PICKUP; + break; + } + default: r->report->unknown++; break; + } + return 1; +} + +static SCHEMA_UNUSED int table_entity_save_body( TableWriter * w, const TableEntity * value ) +{ + (void) value; + { /* entity_id */ + if ( !( value->entity_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x23fcfd6678e36712ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->entity_id ) ); + } + } + { /* pos_x */ + if ( !( value->pos_x == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xcb4b37357667310eull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->pos_x ) ); + } + } + { /* pos_y */ + if ( !( value->pos_y == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xcb4b3835766732c1ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->pos_y ) ); + } + } + { /* pos_z */ + if ( !( value->pos_z == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xcb4b353576672da8ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->pos_z ) ); + } + } + { /* yaw */ + if ( !( value->yaw == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb54d8e19798e16e8ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->yaw ) ); + } + } + { /* pitch */ + if ( !( value->pitch == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x53a9f665a90cc1b1ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->pitch ) ); + } + } + { /* vel_x */ + if ( !( value->vel_x == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6cede6b6eb60ee67ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->vel_x ) ); + } + } + { /* vel_y */ + if ( !( value->vel_y == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6cede5b6eb60ecb4ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->vel_y ) ); + } + } + { /* vel_z */ + if ( !( value->vel_z == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6cede8b6eb60f1cdull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->vel_z ) ); + } + } + { /* health */ + if ( !( value->health == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7f69d4b5288ba9cfull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->health ) ); + } + } + { /* weapon */ + if ( !( value->weapon == TABLE_WEAPON_NONE ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa0b610205f2c6e01ull ); table_writer_put8( w, 30 ); + switch ( value->weapon ) + { + case TABLE_WEAPON_NONE: table_writer_leb( w, 0 ); break; + case TABLE_WEAPON_FISTS: table_writer_id( w, 0xa790eee12766cf0cull ); break; + case TABLE_WEAPON_PISTOL: table_writer_id( w, 0x9fbfefa835da8476ull ); break; + case TABLE_WEAPON_SHOTGUN: table_writer_id( w, 0x188bbb1783928a95ull ); break; + case TABLE_WEAPON_RIFLE: table_writer_id( w, 0x2c3667b9c2f272f1ull ); break; + case TABLE_WEAPON_SNIPER: table_writer_id( w, 0x0ca8b41b00d755f6ull ); break; + case TABLE_WEAPON_SMG: table_writer_id( w, 0x985fc819fab21a4eull ); break; + case TABLE_WEAPON_ROCKET: table_writer_id( w, 0x229d0447c3086a55ull ); break; + case TABLE_WEAPON_GRENADE: table_writer_id( w, 0x011c7b49a7228f9dull ); break; + case TABLE_WEAPON_PLASMA: table_writer_id( w, 0x89f7566e1123e15full ); break; + case TABLE_WEAPON_RAILGUN: table_writer_id( w, 0x8d7f25318b3b4469ull ); break; + case TABLE_WEAPON_FLAMER: table_writer_id( w, 0xd9cd0dcc285b7816ull ); break; + case TABLE_WEAPON_MINE: table_writer_id( w, 0x04dc16aea8ff5276ull ); break; + case TABLE_WEAPON_TURRET: table_writer_id( w, 0x35e53bc341129217ull ); break; + case TABLE_WEAPON_DRONE: table_writer_id( w, 0x2cc8282fb0de8831ull ); break; + case TABLE_WEAPON_REPAIR: table_writer_id( w, 0x20bada21bc8cb334ull ); break; + default: return 0; /* no declared identity */ + } + } + } + { /* damage */ + if ( !( value->damage == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7f6308be8ab37fc0ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->damage ) ); + } + } + { /* moving */ + if ( !( value->moving == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x11a44fc1d1243da7ull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->moving ? 1 : 0 ); + } + } + { /* firing */ + if ( !( value->firing == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7674cfd19b9031caull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->firing ? 1 : 0 ); + } + } + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int64_t table_entity_measure( const TableEntity * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !table_entity_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + +static SCHEMA_UNUSED int64_t table_entity_save( const TableEntity * value, uint8_t * buffer, int64_t capacity ) +{ + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !table_entity_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + +static SCHEMA_UNUSED int table_entity_load_body( TableReader * r, TableEntity * value ) +{ + table_entity_reset( value ); + for ( ;; ) + { + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) + { + case 0x23fcfd6678e36712ull: /* entity_id */ + { + if ( kind != 7 ) + { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4095ull ) { decoded_wide = 4095ull; r->report->clamped++; } + value->entity_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ + value->entity_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 511ull ) { decoded_v = 511ull; r->report->clamped++; } /* bits(9) width clamp */ - value->pitch = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4095ull ) { decoded_wide = 4095ull; r->report->clamped++; } + value->entity_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ + value->entity_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x03e2: /* vel_x */ + case 0xcb4b37357667310eull: /* pos_x */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_x = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_x = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } + else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } + value->pos_x = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } - else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } - value->vel_x = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_x = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_x = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } + else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } + value->pos_x = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x0151: /* vel_y */ + case 0xcb4b3835766732c1ull: /* pos_y */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_y = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_y = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } + else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } + value->pos_y = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } - else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } - value->vel_y = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_y = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_y = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } + else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } + value->pos_y = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x0e88: /* vel_z */ + case 0xcb4b353576672da8ull: /* pos_z */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_z = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_z = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } + else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } + value->pos_z = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } - else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } - value->vel_z = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_z = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -16383ll ) { decoded_wide = -16383ll; r->report->clamped++; } + if ( decoded_wide > 16383ll ) { decoded_wide = 16383ll; r->report->clamped++; } + value->pos_z = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -16383 ) { decoded_v = -16383; r->report->clamped++; } + else if ( decoded_v > 16383 ) { decoded_v = 16383; r->report->clamped++; } + value->pos_z = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x8617: /* health */ + case 0xb54d8e19798e16e8ull: /* yaw */ { - if ( kind != 4 ) + if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 511ull ) { decoded_wide = 511ull; r->report->clamped++; } + value->yaw = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 511ull ) { decoded_v = 511ull; r->report->clamped++; } /* bits(9) width clamp */ + value->yaw = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } - value->health = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 511ull ) { decoded_wide = 511ull; r->report->clamped++; } + value->yaw = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 511ull ) { decoded_v = 511ull; r->report->clamped++; } /* bits(9) width clamp */ + value->yaw = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x4f72: /* weapon */ + case 0x53a9f665a90cc1b1ull: /* pitch */ { if ( kind != 7 ) + { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 511ull ) { decoded_wide = 511ull; r->report->clamped++; } + value->pitch = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 511ull ) { decoded_v = 511ull; r->report->clamped++; } /* bits(9) width clamp */ + value->pitch = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 511ull ) { decoded_wide = 511ull; r->report->clamped++; } + value->pitch = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 511ull ) { decoded_v = 511ull; r->report->clamped++; } /* bits(9) width clamp */ + value->pitch = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + break; + } + case 0x6cede6b6eb60ee67ull: /* vel_x */ + { + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_x = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_x = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } + else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } + value->vel_x = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_x = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_x = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } + else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } + value->vel_x = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + break; + } + case 0x6cede5b6eb60ecb4ull: /* vel_y */ + { + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_y = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_y = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } + else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } + value->vel_y = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_y = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_y = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } + else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } + value->vel_y = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + break; + } + case 0x6cede8b6eb60f1cdull: /* vel_z */ + { + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_z = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_z = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } + else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } + value->vel_z = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_z = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2048ll ) { decoded_wide = -2048ll; r->report->clamped++; } + if ( decoded_wide > 2047ll ) { decoded_wide = 2047ll; r->report->clamped++; } + value->vel_z = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2048 ) { decoded_v = -2048; r->report->clamped++; } + else if ( decoded_v > 2047 ) { decoded_v = 2047; r->report->clamped++; } + value->vel_z = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + break; + } + case 0x7f69d4b5288ba9cfull: /* health */ + { + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->health = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->health = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->health = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->health = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->health = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->health = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + break; + } + case 0xa0b610205f2c6e01ull: /* weapon */ + { + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } - { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) - { - case 0: value->weapon = TABLE_WEAPON_NONE; break; - case 0x03ed: value->weapon = TABLE_WEAPON_FISTS; break; - case 0x54eb: value->weapon = TABLE_WEAPON_PISTOL; break; - case 0xdf27: value->weapon = TABLE_WEAPON_SHOTGUN; break; - case 0xaa64: value->weapon = TABLE_WEAPON_RIFLE; break; - case 0x5513: value->weapon = TABLE_WEAPON_SNIPER; break; - case 0x138b: value->weapon = TABLE_WEAPON_SMG; break; - case 0x56fa: value->weapon = TABLE_WEAPON_ROCKET; break; - case 0xf3ed: value->weapon = TABLE_WEAPON_GRENADE; break; - case 0xa376: value->weapon = TABLE_WEAPON_PLASMA; break; - case 0xeff8: value->weapon = TABLE_WEAPON_RAILGUN; break; - case 0xfa19: value->weapon = TABLE_WEAPON_FLAMER; break; - case 0x61bf: value->weapon = TABLE_WEAPON_MINE; break; - case 0xf720: value->weapon = TABLE_WEAPON_TURRET; break; - case 0xc67e: value->weapon = TABLE_WEAPON_DRONE; break; - case 0x86cc: value->weapon = TABLE_WEAPON_REPAIR; break; - default: value->weapon = TABLE_WEAPON_NONE; r->report->unknown++; break; /* an id this build cannot name */ + { + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->weapon = TABLE_WEAPON_NONE; } + else + { + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0xa790eee12766cf0cull: value->weapon = TABLE_WEAPON_FISTS; break; + case 0x9fbfefa835da8476ull: value->weapon = TABLE_WEAPON_PISTOL; break; + case 0x188bbb1783928a95ull: value->weapon = TABLE_WEAPON_SHOTGUN; break; + case 0x2c3667b9c2f272f1ull: value->weapon = TABLE_WEAPON_RIFLE; break; + case 0x0ca8b41b00d755f6ull: value->weapon = TABLE_WEAPON_SNIPER; break; + case 0x985fc819fab21a4eull: value->weapon = TABLE_WEAPON_SMG; break; + case 0x229d0447c3086a55ull: value->weapon = TABLE_WEAPON_ROCKET; break; + case 0x011c7b49a7228f9dull: value->weapon = TABLE_WEAPON_GRENADE; break; + case 0x89f7566e1123e15full: value->weapon = TABLE_WEAPON_PLASMA; break; + case 0x8d7f25318b3b4469ull: value->weapon = TABLE_WEAPON_RAILGUN; break; + case 0xd9cd0dcc285b7816ull: value->weapon = TABLE_WEAPON_FLAMER; break; + case 0x04dc16aea8ff5276ull: value->weapon = TABLE_WEAPON_MINE; break; + case 0x35e53bc341129217ull: value->weapon = TABLE_WEAPON_TURRET; break; + case 0x2cc8282fb0de8831ull: value->weapon = TABLE_WEAPON_DRONE; break; + case 0x20bada21bc8cb334ull: value->weapon = TABLE_WEAPON_REPAIR; break; + default: value->weapon = TABLE_WEAPON_NONE; r->report->unknown++; break; + } } } break; } - case 0x15a9: /* damage */ + case 0x7f6308be8ab37fc0ull: /* damage */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->damage = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->damage = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->damage = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->damage = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->damage = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->damage = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->damage = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->damage = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->damage = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xa4b2: /* moving */ + case 0x11a44fc1d1243da7ull: /* moving */ { if ( kind != 1 ) { @@ -1131,11 +2783,19 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_entity_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->moving = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->moving = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x2302: /* firing */ + case 0x7674cfd19b9031caull: /* firing */ { if ( kind != 1 ) { @@ -1143,75 +2803,610 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_entity_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->firing = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->firing = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int table_entity_load( TableEntity * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + table_entity_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return table_entity_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t table_stat_measure( const TableStat * value ) +static SCHEMA_UNUSED int table_entity_save_message_body(TableBitWriter * w,const TableEntity * value) { - int64_t bytes = 2; /* terminator */ - if ( value->stat_id != 0 ) { bytes += 3 + 1; } /* stat_id */ - if ( value->delta != 0 ) { bytes += 3 + 4; } /* delta */ - return bytes; + (void)value; + { /* entity_id */ + if(!(value->entity_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,3,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->entity_id)-UINT64_C(0),12); + } + } + { /* pos_x */ + if(!(value->pos_x == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,4,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->pos_x)-UINT64_C(18446744073709535233),15); + } + } + { /* pos_y */ + if(!(value->pos_y == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,5,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->pos_y)-UINT64_C(18446744073709535233),15); + } + } + { /* pos_z */ + if(!(value->pos_z == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,6,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->pos_z)-UINT64_C(18446744073709535233),15); + } + } + { /* yaw */ + if(!(value->yaw == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,7,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->yaw)-UINT64_C(0),9); + } + } + { /* pitch */ + if(!(value->pitch == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,8,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->pitch)-UINT64_C(0),9); + } + } + { /* vel_x */ + if(!(value->vel_x == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,9,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->vel_x)-UINT64_C(18446744073709549568),12); + } + } + { /* vel_y */ + if(!(value->vel_y == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,10,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->vel_y)-UINT64_C(18446744073709549568),12); + } + } + { /* vel_z */ + if(!(value->vel_z == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,11,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->vel_z)-UINT64_C(18446744073709549568),12); + } + } + { /* health */ + if(!(value->health == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,12,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->health)-UINT64_C(0),10); + } + } + { /* weapon */ + if(!(value->weapon == TABLE_WEAPON_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + switch(value->weapon) { + case TABLE_WEAPON_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_FISTS: table_bit_put(w,53,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_PISTOL: table_bit_put(w,54,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_SHOTGUN: table_bit_put(w,55,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_RIFLE: table_bit_put(w,56,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_SNIPER: table_bit_put(w,57,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_SMG: table_bit_put(w,58,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_ROCKET: table_bit_put(w,59,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_GRENADE: table_bit_put(w,60,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_PLASMA: table_bit_put(w,61,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_RAILGUN: table_bit_put(w,62,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_FLAMER: table_bit_put(w,63,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_MINE: table_bit_put(w,64,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_TURRET: table_bit_put(w,65,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_DRONE: table_bit_put(w,66,kTableMessageRefBitsHere); + break; + case TABLE_WEAPON_REPAIR: table_bit_put(w,67,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* damage */ + if(!(value->damage == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->damage)-UINT64_C(0),8); + } + } + { /* moving */ + if(!(value->moving == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,15,kTableMessageRefBitsHere); + table_bit_put(w,value->moving ? 1 : 0,1); + } + } + { /* firing */ + if(!(value->firing == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,16,kTableMessageRefBitsHere); + table_bit_put(w,value->firing ? 1 : 0,1); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_stat_save_body( TableWriter * w, const TableStat * value ) +static SCHEMA_UNUSED int64_t table_entity_measure_messages(const TableEntity * values,int64_t count,TableReport * report) { - if ( value->stat_id != 0 ) - { - table_writer_put16( w, 0xfb6c ); table_writer_put8( w, 6 ); /* stat_id */ - table_writer_put8( w, (uint8_t) ( value->stat_id ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x23fcfd6678e36712): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4095ull){decoded=4095ull; + r->report->clamped++; + } + value->entity_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xcb4b37357667310e): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-16383ll){decoded=-16383ll; + r->report->clamped++; + } + if(decoded>16383ll){decoded=16383ll; + r->report->clamped++; + } + value->pos_x=(int32_t)decoded; + } + break; + } + case UINT64_C(0xcb4b3835766732c1): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-16383ll){decoded=-16383ll; + r->report->clamped++; + } + if(decoded>16383ll){decoded=16383ll; + r->report->clamped++; + } + value->pos_y=(int32_t)decoded; + } + break; + } + case UINT64_C(0xcb4b353576672da8): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-16383ll){decoded=-16383ll; + r->report->clamped++; + } + if(decoded>16383ll){decoded=16383ll; + r->report->clamped++; + } + value->pos_z=(int32_t)decoded; + } + break; + } + case UINT64_C(0xb54d8e19798e16e8): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>511ull){decoded=511ull; + r->report->clamped++; + } + value->yaw=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x53a9f665a90cc1b1): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>511ull){decoded=511ull; + r->report->clamped++; + } + value->pitch=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x6cede6b6eb60ee67): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2048ll){decoded=-2048ll; + r->report->clamped++; + } + if(decoded>2047ll){decoded=2047ll; + r->report->clamped++; + } + value->vel_x=(int32_t)decoded; + } + break; + } + case UINT64_C(0x6cede5b6eb60ecb4): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2048ll){decoded=-2048ll; + r->report->clamped++; + } + if(decoded>2047ll){decoded=2047ll; + r->report->clamped++; + } + value->vel_y=(int32_t)decoded; + } + break; + } + case UINT64_C(0x6cede8b6eb60f1cd): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2048ll){decoded=-2048ll; + r->report->clamped++; + } + if(decoded>2047ll){decoded=2047ll; + r->report->clamped++; + } + value->vel_z=(int32_t)decoded; + } + break; + } + case UINT64_C(0x7f69d4b5288ba9cf): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>1000ll){decoded=1000ll; + r->report->clamped++; + } + value->health=(int32_t)decoded; + } + break; + } + case UINT64_C(0xa0b610205f2c6e01): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->weapon=TABLE_WEAPON_NONE; + else switch(named->id) { + case UINT64_C(0xa790eee12766cf0c): value->weapon=TABLE_WEAPON_FISTS; + break; + case UINT64_C(0x9fbfefa835da8476): value->weapon=TABLE_WEAPON_PISTOL; + break; + case UINT64_C(0x188bbb1783928a95): value->weapon=TABLE_WEAPON_SHOTGUN; + break; + case UINT64_C(0x2c3667b9c2f272f1): value->weapon=TABLE_WEAPON_RIFLE; + break; + case UINT64_C(0x0ca8b41b00d755f6): value->weapon=TABLE_WEAPON_SNIPER; + break; + case UINT64_C(0x985fc819fab21a4e): value->weapon=TABLE_WEAPON_SMG; + break; + case UINT64_C(0x229d0447c3086a55): value->weapon=TABLE_WEAPON_ROCKET; + break; + case UINT64_C(0x011c7b49a7228f9d): value->weapon=TABLE_WEAPON_GRENADE; + break; + case UINT64_C(0x89f7566e1123e15f): value->weapon=TABLE_WEAPON_PLASMA; + break; + case UINT64_C(0x8d7f25318b3b4469): value->weapon=TABLE_WEAPON_RAILGUN; + break; + case UINT64_C(0xd9cd0dcc285b7816): value->weapon=TABLE_WEAPON_FLAMER; + break; + case UINT64_C(0x04dc16aea8ff5276): value->weapon=TABLE_WEAPON_MINE; + break; + case UINT64_C(0x35e53bc341129217): value->weapon=TABLE_WEAPON_TURRET; + break; + case UINT64_C(0x2cc8282fb0de8831): value->weapon=TABLE_WEAPON_DRONE; + break; + case UINT64_C(0x20bada21bc8cb334): value->weapon=TABLE_WEAPON_REPAIR; + break; + default:value->weapon=TABLE_WEAPON_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0x7f6308be8ab37fc0): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->damage=(TableDamage)decoded; + } + break; + } + case UINT64_C(0x11a44fc1d1243da7): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->moving=lo!=0; + } + break; + } + case UINT64_C(0x7674cfd19b9031ca): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->firing=lo!=0; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int table_entity_load_messages(TableEntity * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_stat_save_body( TableWriter * w, const TableStat * value ) +{ + (void) value; + { /* stat_id */ + if ( !( value->stat_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x80ab75f0866dbf65ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->stat_id ) ); + } } - if ( value->delta != 0 ) - { - table_writer_put16( w, 0x1720 ); table_writer_put8( w, 4 ); /* delta */ - table_writer_put32( w, (uint32_t) ( value->delta ) ); + { /* delta */ + if ( !( value->delta == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x52076675ec13a0c1ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->delta ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t table_stat_measure( const TableStat * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !table_stat_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t table_stat_save( const TableStat * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !table_stat_save_body( &w, value ) ) { return -1; } - return w.offset; /* == table_stat_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_stat_load_body( TableReader * r, TableStat * value ) +static SCHEMA_UNUSED int table_stat_load_body( TableReader * r, TableStat * value ) { - table_stat_reset( value ); /* prefill declared defaults in place, then overlay */ + table_stat_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xfb6c: /* stat_id */ + case 0x80ab75f0866dbf65ull: /* stat_id */ { if ( kind != 6 ) { @@ -1219,525 +3414,1375 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_stat_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->stat_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->stat_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x1720: /* delta */ + case 0x52076675ec13a0c1ull: /* delta */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -512ll ) { decoded_wide = -512ll; r->report->clamped++; } + if ( decoded_wide > 511ll ) { decoded_wide = 511ll; r->report->clamped++; } + value->delta = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -512ll ) { decoded_wide = -512ll; r->report->clamped++; } + if ( decoded_wide > 511ll ) { decoded_wide = 511ll; r->report->clamped++; } + value->delta = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -512 ) { decoded_v = -512; r->report->clamped++; } + else if ( decoded_v > 511 ) { decoded_v = 511; r->report->clamped++; } + value->delta = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -512 ) { decoded_v = -512; r->report->clamped++; } - else if ( decoded_v > 511 ) { decoded_v = 511; r->report->clamped++; } - value->delta = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -512ll ) { decoded_wide = -512ll; r->report->clamped++; } + if ( decoded_wide > 511ll ) { decoded_wide = 511ll; r->report->clamped++; } + value->delta = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -512ll ) { decoded_wide = -512ll; r->report->clamped++; } + if ( decoded_wide > 511ll ) { decoded_wide = 511ll; r->report->clamped++; } + value->delta = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -512 ) { decoded_v = -512; r->report->clamped++; } + else if ( decoded_v > 511 ) { decoded_v = 511; r->report->clamped++; } + value->delta = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int table_stat_load( TableStat * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + table_stat_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return table_stat_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t table_mixed_measure( const TableMixed * value ) +static SCHEMA_UNUSED int table_stat_save_message_body(TableBitWriter * w,const TableStat * value) +{ + (void)value; + { /* stat_id */ + if(!(value->stat_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,51,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->stat_id)-UINT64_C(0),8); + } + } + { /* delta */ + if(!(value->delta == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,52,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->delta)-UINT64_C(18446744073709551104),10); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t table_stat_measure_messages(const TableStat * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iprotocol_magic != 0 ) { bytes += 3 + 2; } /* protocol_magic */ - if ( value->sequence != 0 ) { bytes += 3 + 2; } /* sequence */ - if ( value->ack_sequence != 0 ) { bytes += 3 + 4; } /* ack_sequence */ - if ( value->ack_bits != 0 ) { bytes += 3 + 4; } /* ack_bits */ - if ( value->session_id != 0 ) { bytes += 3 + 8; } /* session_id */ - if ( value->client_id != 0 ) { bytes += 3 + 4; } /* client_id */ - if ( value->nonce != 1ull ) { bytes += 3 + 8; } /* nonce */ - if ( value->world_time != 0 ) { bytes += 3 + 8; } /* world_time */ - if ( value->frame_tick != 0 ) { bytes += 3 + 8; } /* frame_tick */ - if ( value->server_time != 0.0f ) { bytes += 3 + 4; } /* server_time */ - if ( value->entities_count < 0 || value->entities_count > 8 ) { return -1; } /* storage invariant */ - if ( value->entities_count > 0 ) + TableBitWriter w=table_bit_writer(buffer,capacity); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x80ab75f0866dbf65): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->stat_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x52076675ec13a0c1): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-512ll){decoded=-512ll; + r->report->clamped++; + } + if(decoded>511ll){decoded=511ll; + r->report->clamped++; + } + value->delta=(int32_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int table_stat_load_messages(TableStat * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_benchtable_table_mixed_wire_entities_( TableWriter * w, const TableMixed * value ) +{ + if ( value->entities_count < 0 || value->entities_count > 8 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->entities_count ); + for ( i = 0; i < value->entities_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* entities */ - for ( i = 0; i < value->entities_count; i++ ) + if ( w->buffer == NULL ) { - int64_t elem_entities = table_entity_measure( &value->entities[i] ); - if ( elem_entities < 0 ) { return -1; } - bytes += 4 + elem_entities; + int64_t frame_begin = w->offset; + if ( !table_entity_save_body( w, &value->entities[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - } - if ( value->stats_count < 0 || value->stats_count > 80 ) { return -1; } /* storage invariant */ - if ( value->stats_count > 0 ) - { - int32_t i; - bytes += 3 + 4 + 5; /* stats */ - for ( i = 0; i < value->stats_count; i++ ) + else { - int64_t elem_stats = table_stat_measure( &value->stats[i] ); - if ( elem_stats < 0 ) { return -1; } - bytes += 4 + elem_stats; + TableWriter probe = table_writer_probe( w ); + if ( !table_entity_save_body( &probe, &value->entities[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !table_entity_save_body( w, &value->entities[i] ) ) { return 0; } } } - switch ( value->game_event.type ) /* game_event */ + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_benchtable_table_mixed_wire_stats_( TableWriter * w, const TableMixed * value ) +{ + if ( value->stats_count < 0 || value->stats_count > 80 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->stats_count ); + for ( i = 0; i < value->stats_count; i++ ) { - case TABLE_EVENT_TYPE_NONE: break; /* None elides — TLV absence is the None */ - case TABLE_EVENT_TYPE_HIT: + if ( w->buffer == NULL ) { - int64_t arm_game_event = table_hit_event_measure( &value->game_event.as.hit ); - if ( arm_game_event < 0 ) { return -1; } - bytes += 3 + 2 + 4 + arm_game_event; /* the u16 ARM ID, then the arm length-prefixed */ - break; + int64_t frame_begin = w->offset; + if ( !table_stat_save_body( w, &value->stats[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - case TABLE_EVENT_TYPE_CHAT: + else { - int64_t arm_game_event = table_chat_event_measure( &value->game_event.as.chat ); - if ( arm_game_event < 0 ) { return -1; } - bytes += 3 + 2 + 4 + arm_game_event; /* the u16 ARM ID, then the arm length-prefixed */ - break; + TableWriter probe = table_writer_probe( w ); + if ( !table_stat_save_body( &probe, &value->stats[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !table_stat_save_body( w, &value->stats[i] ) ) { return 0; } } - case TABLE_EVENT_TYPE_PICKUP: - { - int64_t arm_game_event = table_pickup_event_measure( &value->game_event.as.pickup ); - if ( arm_game_event < 0 ) { return -1; } - bytes += 3 + 2 + 4 + arm_game_event; /* the u16 ARM ID, then the arm length-prefixed */ - break; - } - default: return -1; /* invalid tag — the write side refuses it too */ } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_benchtable_table_mixed_wire_loadout_( TableWriter * w, const TableMixed * value ) +{ + int32_t i; + table_writer_put8( w, 6 ); table_writer_leb( w, (uint64_t) 4 ); + for ( i = 0; i < 4; i++ ) { - int all_default_loadout = 1; - int32_t i; - for ( i = 0; i < 4; i++ ) { if ( value->loadout[i] != 0 ) { all_default_loadout = 0; break; } } - if ( !all_default_loadout ) - { - bytes += 3 + 4 + 5 + 4; /* loadout */ - } - } - if ( value->player_name_length < 0 || value->player_name_length > 15 ) { return -1; } /* storage invariant */ - if ( value->player_name_length > 0 ) { bytes += 3 + 4 + value->player_name_length; } /* player_name */ - if ( value->payload_length < 0 || value->payload_length > 16 ) { return -1; } /* storage invariant */ - if ( value->payload_length > 0 ) { bytes += 3 + 4 + 5 + value->payload_length; } /* payload */ - if ( value->aim_x != 0.0f ) { bytes += 3 + 4; } /* aim_x */ - if ( value->aim_y != 0.0f ) { bytes += 3 + 4; } /* aim_y */ - if ( value->aim_z != 0.0f ) { bytes += 3 + 4; } /* aim_z */ - if ( value->recoil != 0.0f ) { bytes += 3 + 4; } /* recoil */ - if ( value->drift != 0.0 ) { bytes += 3 + 8; } /* drift */ - if ( value->wide_key != 0 ) { bytes += 3 + 8; } /* wide_key */ - if ( value->flux != 0 ) { bytes += 3 + 8; } /* flux */ - if ( value->ping != 0.0f ) { bytes += 3 + 4; } /* ping */ - if ( value->crc_hint != 0 ) { bytes += 3 + 4; } /* crc_hint */ - if ( value->has_extra != 0 ) { bytes += 3 + 1; } /* has_extra */ - if ( value->has_extra ) - { - if ( value->extra != 0 ) { bytes += 3 + 4; } /* extra */ - } - if ( !value->has_extra ) - { - if ( value->idle_ticks != 0 ) { bytes += 3 + 4; } /* idle_ticks */ + table_writer_put8( w, (uint8_t) ( value->loadout[i] ) ); } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_save_body( TableWriter * w, const TableMixed * value ) +static SCHEMA_UNUSED int schema_benchtable_table_mixed_wire_player_name_( TableWriter * w, const TableMixed * value ) { - if ( value->protocol_magic != 0 ) - { - table_writer_put16( w, 0xae30 ); table_writer_put8( w, 7 ); /* protocol_magic */ - table_writer_put16( w, (uint16_t) ( value->protocol_magic ) ); + if ( value->player_name_length < 0 || value->player_name_length > 15 ) { return 0; } + table_writer_raw( w, value->player_name, value->player_name_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_benchtable_table_mixed_wire_payload_( TableWriter * w, const TableMixed * value ) +{ + if ( value->payload_length < 0 || value->payload_length > 16 ) { return 0; } + table_writer_put8( w, 6 ); table_writer_leb( w, (uint64_t) value->payload_length ); + table_writer_raw( w, value->payload, value->payload_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int table_mixed_save_body( TableWriter * w, const TableMixed * value ) +{ + (void) value; + { /* protocol_magic */ + if ( !( value->protocol_magic == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6a5a70d91aa115fdull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->protocol_magic ) ); + } } - if ( value->sequence != 0 ) - { - table_writer_put16( w, 0xd32b ); table_writer_put8( w, 7 ); /* sequence */ - table_writer_put16( w, (uint16_t) ( value->sequence ) ); + { /* sequence */ + if ( !( value->sequence == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaa38aca481f528a8ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->sequence ) ); + } } - if ( value->ack_sequence != 0 ) - { - table_writer_put16( w, 0x3363 ); table_writer_put8( w, 4 ); /* ack_sequence */ - table_writer_put32( w, (uint32_t) ( value->ack_sequence ) ); + { /* ack_sequence */ + if ( !( value->ack_sequence == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0dbe005c56697c3eull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->ack_sequence ) ); + } } - if ( value->ack_bits != 0 ) - { - table_writer_put16( w, 0xebb9 ); table_writer_put8( w, 8 ); /* ack_bits */ - table_writer_put32( w, (uint32_t) ( value->ack_bits ) ); + { /* ack_bits */ + if ( !( value->ack_bits == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x9bae0da8b829ee03ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->ack_bits ) ); + } } - if ( value->session_id != 0 ) - { - table_writer_put16( w, 0x8790 ); table_writer_put8( w, 9 ); /* session_id */ - table_writer_put64( w, (uint64_t) ( value->session_id ) ); + { /* session_id */ + if ( !( value->session_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb7d7b5650a590b05ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->session_id ) ); + } } - if ( value->client_id != 0 ) - { - table_writer_put16( w, 0xd443 ); table_writer_put8( w, 8 ); /* client_id */ - table_writer_put32( w, (uint32_t) ( value->client_id ) ); + { /* client_id */ + if ( !( value->client_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6d7b98e2d095967eull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->client_id ) ); + } } - if ( value->nonce != 1ull ) - { - table_writer_put16( w, 0x80f0 ); table_writer_put8( w, 9 ); /* nonce */ - table_writer_put64( w, (uint64_t) ( value->nonce ) ); + { /* nonce */ + if ( !( value->nonce == 1ull ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x73a94c71d60dc0d8ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->nonce ) ); + } } - if ( value->world_time != 0 ) - { - table_writer_put16( w, 0x77f2 ); table_writer_put8( w, 5 ); /* world_time */ - table_writer_put64( w, (uint64_t) ( value->world_time ) ); + { /* world_time */ + if ( !( value->world_time == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x3eee6b51be54fc85ull ); table_writer_put8( w, 5 ); + table_writer_put64( w, (uint64_t) ( value->world_time ) ); + } } - if ( value->frame_tick != 0 ) - { - table_writer_put16( w, 0xcbc2 ); table_writer_put8( w, 9 ); /* frame_tick */ - table_writer_put64( w, (uint64_t) ( value->frame_tick ) ); + { /* frame_tick */ + if ( !( value->frame_tick == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7bbc035f7b6d0112ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->frame_tick ) ); + } } - if ( value->server_time != 0.0f ) - { - table_writer_put16( w, 0x27f9 ); table_writer_put8( w, 10 ); /* server_time */ - table_writer_put32( w, table_float_to_bits( value->server_time ) ); + { /* server_time */ + if ( !( value->server_time == 0.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x3c460475f9be69c6ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->server_time ) ); + } } - if ( value->entities_count < 0 || value->entities_count > 8 ) { return 0; } /* storage invariant */ - if ( value->entities_count > 0 ) - { - int64_t len_at_entities; - int32_t i; - table_writer_put16( w, 0x25e3 ); table_writer_put8( w, 14 ); /* entities */ - len_at_entities = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->entities_count ); - for ( i = 0; i < value->entities_count; i++ ) + { /* entities */ + if ( value->entities_count < 0 || value->entities_count > 8 ) { return 0; } + if ( value->entities_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x935d0fb07bb3822aull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_benchtable_table_mixed_wire_entities_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !table_entity_save_body( w, &value->entities[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_benchtable_table_mixed_wire_entities_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_benchtable_table_mixed_wire_entities_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_entities, (uint32_t) ( w->offset - len_at_entities - 4 ) ); } - if ( value->stats_count < 0 || value->stats_count > 80 ) { return 0; } /* storage invariant */ - if ( value->stats_count > 0 ) - { - int64_t len_at_stats; - int32_t i; - table_writer_put16( w, 0x76dd ); table_writer_put8( w, 14 ); /* stats */ - len_at_stats = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->stats_count ); - for ( i = 0; i < value->stats_count; i++ ) + { /* stats */ + if ( value->stats_count < 0 || value->stats_count > 80 ) { return 0; } + if ( value->stats_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xee639cad45b1994cull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_benchtable_table_mixed_wire_stats_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !table_stat_save_body( w, &value->stats[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_benchtable_table_mixed_wire_stats_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_benchtable_table_mixed_wire_stats_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_stats, (uint32_t) ( w->offset - len_at_stats - 4 ) ); } - if ( value->game_event.type != TABLE_EVENT_TYPE_NONE ) - { - int64_t len_at_game_event; - table_writer_put16( w, 0xa17e ); table_writer_put8( w, 15 ); /* game_event */ - /* the ARM ID is the hash of the arm's NAME (docs/SPEC-TABLES.md §5), so - arms may be added anywhere, removed and reordered */ - switch ( value->game_event.type ) + { /* game_event */ + if ( value->game_event.type != TABLE_EVENT_TYPE_NONE ) { - case TABLE_EVENT_TYPE_HIT: table_writer_put16( w, 0xba78 ); break; - case TABLE_EVENT_TYPE_CHAT: table_writer_put16( w, 0x5be0 ); break; - case TABLE_EVENT_TYPE_PICKUP: table_writer_put16( w, 0x99dd ); break; - default: return 0; /* write validates the tag before it rides */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x2e35dc5321aa5790ull ); table_writer_put8( w, 15 ); + if ( !schema_benchtable_table_event_wire_save_( w, &value->game_event ) ) { return 0; } } - len_at_game_event = w->offset; table_writer_put32( w, 0 ); - switch ( value->game_event.type ) + } + { /* loadout */ + int rides = 0; int32_t i; + for ( i = 0; i < 4; i++ ) { if ( !( value->loadout[i] == 0 ) ) { rides = 1; break; } } + if ( rides ) { - case TABLE_EVENT_TYPE_HIT: if ( !table_hit_event_save_body( w, &value->game_event.as.hit ) ) { return 0; } break; - case TABLE_EVENT_TYPE_CHAT: if ( !table_chat_event_save_body( w, &value->game_event.as.chat ) ) { return 0; } break; - case TABLE_EVENT_TYPE_PICKUP: if ( !table_pickup_event_save_body( w, &value->game_event.as.pickup ) ) { return 0; } break; - default: return 0; /* write validates the tag before it rides */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5759ce7586bbb5a3ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_benchtable_table_mixed_wire_loadout_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_benchtable_table_mixed_wire_loadout_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_benchtable_table_mixed_wire_loadout_( w, value ) ) { return 0; } + } } - table_writer_patch32( w, len_at_game_event, (uint32_t) ( w->offset - len_at_game_event - 4 ) ); } - { - int all_default_loadout = 1; - int32_t i; - for ( i = 0; i < 4; i++ ) { if ( value->loadout[i] != 0 ) { all_default_loadout = 0; break; } } - if ( !all_default_loadout ) + { /* player_name */ + if ( value->player_name_length < 0 || value->player_name_length > 15 ) { return 0; } + if ( value->player_name_length != 0 ) { - int64_t len_at_loadout; - table_writer_put16( w, 0x9f78 ); table_writer_put8( w, 14 ); /* loadout (fixed [4]) */ - len_at_loadout = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 6 ); table_writer_put32( w, 4 ); - for ( i = 0; i < 4; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x13a62f542cf8e86eull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_benchtable_table_mixed_wire_player_name_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - table_writer_put8( w, (uint8_t) ( value->loadout[i] ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_benchtable_table_mixed_wire_player_name_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_benchtable_table_mixed_wire_player_name_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_loadout, (uint32_t) ( w->offset - len_at_loadout - 4 ) ); } } - if ( value->player_name_length < 0 || value->player_name_length > 15 ) { return 0; } /* storage invariant */ - if ( value->player_name_length > 0 ) - { - table_writer_put16( w, 0x2d3e ); table_writer_put8( w, 12 ); /* player_name */ - table_writer_put32( w, (uint32_t) value->player_name_length ); - table_writer_raw( w, value->player_name, value->player_name_length ); - } - if ( value->payload_length < 0 || value->payload_length > 16 ) { return 0; } /* storage invariant */ - if ( value->payload_length > 0 ) - { - table_writer_put16( w, 0x44aa ); table_writer_put8( w, 14 ); /* payload */ - table_writer_put32( w, (uint32_t) ( 5 + value->payload_length ) ); - table_writer_put8( w, 6 ); table_writer_put32( w, (uint32_t) value->payload_length ); - table_writer_raw( w, value->payload, value->payload_length ); + { /* payload */ + if ( value->payload_length < 0 || value->payload_length > 16 ) { return 0; } + if ( value->payload_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xcfb8a9d063b5e9e5ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_benchtable_table_mixed_wire_payload_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_benchtable_table_mixed_wire_payload_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_benchtable_table_mixed_wire_payload_( w, value ) ) { return 0; } + } + } } - if ( value->aim_x != 0.0f ) - { - table_writer_put16( w, 0x84e9 ); table_writer_put8( w, 10 ); /* aim_x */ - table_writer_put32( w, table_float_to_bits( value->aim_x ) ); + { /* aim_x */ + if ( !( value->aim_x == 0.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xdbaf7be5e24296c9ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->aim_x ) ); + } } - if ( value->aim_y != 0.0f ) - { - table_writer_put16( w, 0x8d96 ); table_writer_put8( w, 10 ); /* aim_y */ - table_writer_put32( w, table_float_to_bits( value->aim_y ) ); + { /* aim_y */ + if ( !( value->aim_y == 0.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xdbaf7ae5e2429516ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->aim_y ) ); + } } - if ( value->aim_z != 0.0f ) - { - table_writer_put16( w, 0x8e03 ); table_writer_put8( w, 10 ); /* aim_z */ - table_writer_put32( w, table_float_to_bits( value->aim_z ) ); + { /* aim_z */ + if ( !( value->aim_z == 0.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xdbaf79e5e2429363ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->aim_z ) ); + } } - if ( value->recoil != 0.0f ) - { - table_writer_put16( w, 0x2d04 ); table_writer_put8( w, 10 ); /* recoil */ - table_writer_put32( w, table_float_to_bits( value->recoil ) ); + { /* recoil */ + if ( !( value->recoil == 0.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x9cef31fff7e2e457ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->recoil ) ); + } } - if ( value->drift != 0.0 ) - { - table_writer_put16( w, 0xc023 ); table_writer_put8( w, 11 ); /* drift */ - table_writer_put64( w, table_double_to_bits( value->drift ) ); + { /* drift */ + if ( !( value->drift == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5ab3f9c9341f6c04ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->drift ) ); + } } - if ( value->wide_key != 0 ) - { - table_writer_put16( w, 0x272f ); table_writer_put8( w, 9 ); /* wide_key */ - table_writer_put64( w, (uint64_t) ( value->wide_key ) ); + { /* wide_key */ + if ( !( value->wide_key == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa3348580461faf16ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->wide_key ) ); + } } - if ( value->flux != 0 ) - { - table_writer_put16( w, 0x196a ); table_writer_put8( w, 5 ); /* flux */ - table_writer_put64( w, (uint64_t) ( value->flux ) ); + { /* flux */ + if ( !( value->flux == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd61bdd7908af2642ull ); table_writer_put8( w, 5 ); + table_writer_put64( w, (uint64_t) ( value->flux ) ); + } } - if ( value->ping != 0.0f ) - { - table_writer_put16( w, 0xe6d4 ); table_writer_put8( w, 10 ); /* ping */ - table_writer_put32( w, table_float_to_bits( value->ping ) ); + { /* ping */ + if ( !( value->ping == 0.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbf30e00dc53307a9ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->ping ) ); + } } - if ( value->crc_hint != 0 ) - { - table_writer_put16( w, 0xd3dc ); table_writer_put8( w, 8 ); /* crc_hint */ - table_writer_put32( w, (uint32_t) ( value->crc_hint ) ); + { /* crc_hint */ + if ( !( value->crc_hint == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x560d6527ccd8515full ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->crc_hint ) ); + } } - if ( value->has_extra != 0 ) - { - table_writer_put16( w, 0xb023 ); table_writer_put8( w, 1 ); /* has_extra */ - table_writer_put8( w, value->has_extra ? 1 : 0 ); + { /* has_extra */ + if ( !( value->has_extra == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc08292176cfd8672ull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->has_extra ? 1 : 0 ); + } } + { /* extra */ if ( value->has_extra ) { - if ( value->extra != 0 ) + if ( !( value->extra == 0 ) ) { - table_writer_put16( w, 0xb579 ); table_writer_put8( w, 4 ); /* extra */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfd29ee12a979cb69ull ); table_writer_put8( w, 4 ); table_writer_put32( w, (uint32_t) ( value->extra ) ); } } + } + { /* idle_ticks */ if ( !value->has_extra ) { - if ( value->idle_ticks != 0 ) + if ( !( value->idle_ticks == 0 ) ) { - table_writer_put16( w, 0x9555 ); table_writer_put8( w, 4 ); /* idle_ticks */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x78101ac0aa8cbcfeull ); table_writer_put8( w, 4 ); table_writer_put32( w, (uint32_t) ( value->idle_ticks ) ); } } - table_writer_put16( w, 0 ); /* terminator */ + } + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t table_mixed_measure( const TableMixed * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !table_mixed_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t table_mixed_save( const TableMixed * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !table_mixed_save_body( &w, value ) ) { return -1; } - return w.offset; /* == table_mixed_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( TableReader * r, TableMixed * value ) +static SCHEMA_UNUSED int table_mixed_load_body( TableReader * r, TableMixed * value ) { - table_mixed_reset( value ); /* prefill declared defaults in place, then overlay */ + table_mixed_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xae30: /* protocol_magic */ + case 0x6a5a70d91aa115fdull: /* protocol_magic */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->protocol_magic = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->protocol_magic = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - value->protocol_magic = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->protocol_magic = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->protocol_magic = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xd32b: /* sequence */ + case 0xaa38aca481f528a8ull: /* sequence */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 65535ull ) { decoded_wide = 65535ull; r->report->clamped++; } + value->sequence = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - value->sequence = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 65535ull ) { decoded_wide = 65535ull; r->report->clamped++; } + value->sequence = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x3363: /* ack_sequence */ + case 0x0dbe005c56697c3eull: /* ack_sequence */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 65535ll ) { decoded_wide = 65535ll; r->report->clamped++; } + value->ack_sequence = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 65535ll ) { decoded_wide = 65535ll; r->report->clamped++; } + value->ack_sequence = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 65535 ) { decoded_v = 65535; r->report->clamped++; } + value->ack_sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 65535 ) { decoded_v = 65535; r->report->clamped++; } - value->ack_sequence = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 65535ll ) { decoded_wide = 65535ll; r->report->clamped++; } + value->ack_sequence = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 65535ll ) { decoded_wide = 65535ll; r->report->clamped++; } + value->ack_sequence = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 65535 ) { decoded_v = 65535; r->report->clamped++; } + value->ack_sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xebb9: /* ack_bits */ + case 0x9bae0da8b829ee03ull: /* ack_bits */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->ack_bits = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->ack_bits = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->ack_bits = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->ack_bits = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->ack_bits = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->ack_bits = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->ack_bits = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x8790: /* session_id */ + case 0xb7d7b5650a590b05ull: /* session_id */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->session_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->session_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->session_id = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->session_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->session_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->session_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->session_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->session_id = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->session_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xd443: /* client_id */ + case 0x6d7b98e2d095967eull: /* client_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->client_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->client_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->client_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->client_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->client_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->client_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->client_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x80f0: /* nonce */ + case 0x73a94c71d60dc0d8ull: /* nonce */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 9223372036854775807ull ) { decoded_wide = 9223372036854775807ull; r->report->clamped++; } + value->nonce = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 9223372036854775807ull ) { decoded_wide = 9223372036854775807ull; r->report->clamped++; } + value->nonce = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 9223372036854775807ull ) { decoded_wide = 9223372036854775807ull; r->report->clamped++; } + value->nonce = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } + else if ( decoded_v > 9223372036854775807ull ) { decoded_v = 9223372036854775807ull; r->report->clamped++; } + value->nonce = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } - else if ( decoded_v > 9223372036854775807ull ) { decoded_v = 9223372036854775807ull; r->report->clamped++; } - value->nonce = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 9223372036854775807ull ) { decoded_wide = 9223372036854775807ull; r->report->clamped++; } + value->nonce = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 9223372036854775807ull ) { decoded_wide = 9223372036854775807ull; r->report->clamped++; } + value->nonce = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 9223372036854775807ull ) { decoded_wide = 9223372036854775807ull; r->report->clamped++; } + value->nonce = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } + else if ( decoded_v > 9223372036854775807ull ) { decoded_v = 9223372036854775807ull; r->report->clamped++; } + value->nonce = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x77f2: /* world_time */ + case 0x3eee6b51be54fc85ull: /* world_time */ { if ( kind != 5 ) { + if ( table_kind_widens( kind, 5 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -1000000000000ll ) { decoded_wide = -1000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000ll ) { decoded_wide = 1000000000000ll; r->report->clamped++; } + value->world_time = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -1000000000000ll ) { decoded_wide = -1000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000ll ) { decoded_wide = 1000000000000ll; r->report->clamped++; } + value->world_time = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < -1000000000000ll ) { decoded_wide = -1000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000ll ) { decoded_wide = 1000000000000ll; r->report->clamped++; } + value->world_time = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v < -1000000000000ll ) { decoded_v = -1000000000000ll; r->report->clamped++; } + else if ( decoded_v > 1000000000000ll ) { decoded_v = 1000000000000ll; r->report->clamped++; } + value->world_time = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); - if ( decoded_v < -1000000000000ll ) { decoded_v = -1000000000000ll; r->report->clamped++; } - else if ( decoded_v > 1000000000000ll ) { decoded_v = 1000000000000ll; r->report->clamped++; } - value->world_time = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -1000000000000ll ) { decoded_wide = -1000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000ll ) { decoded_wide = 1000000000000ll; r->report->clamped++; } + value->world_time = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -1000000000000ll ) { decoded_wide = -1000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000ll ) { decoded_wide = 1000000000000ll; r->report->clamped++; } + value->world_time = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < -1000000000000ll ) { decoded_wide = -1000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000ll ) { decoded_wide = 1000000000000ll; r->report->clamped++; } + value->world_time = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v < -1000000000000ll ) { decoded_v = -1000000000000ll; r->report->clamped++; } + else if ( decoded_v > 1000000000000ll ) { decoded_v = 1000000000000ll; r->report->clamped++; } + value->world_time = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xcbc2: /* frame_tick */ + case 0x7bbc035f7b6d0112ull: /* frame_tick */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->frame_tick = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->frame_tick = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->frame_tick = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v > 281474976710655ull ) { decoded_v = 281474976710655ull; r->report->clamped++; } /* bits(48) width clamp */ + value->frame_tick = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - if ( decoded_v > 281474976710655ull ) { decoded_v = 281474976710655ull; r->report->clamped++; } /* bits(48) width clamp */ - value->frame_tick = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->frame_tick = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->frame_tick = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->frame_tick = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v > 281474976710655ull ) { decoded_v = 281474976710655ull; r->report->clamped++; } /* bits(48) width clamp */ + value->frame_tick = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x27f9: /* server_time */ + case 0x3c460475f9be69c6ull: /* server_time */ { if ( kind != 10 ) { @@ -1745,16 +4790,24 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); - if ( decoded_f < 0.0f ) { decoded_f = 0.0f; r->report->clamped++; } - else if ( decoded_f > 65535.0f ) { decoded_f = 65535.0f; r->report->clamped++; } - value->server_time = decoded_f; + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); + if ( decoded_f < 0.0f ) { decoded_f = 0.0f; r->report->clamped++; } + else if ( decoded_f > 65535.0f ) { decoded_f = 65535.0f; r->report->clamped++; } + value->server_time = decoded_f; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x25e3: /* entities */ + case 0x935d0fb07bb3822aull: /* entities */ { if ( kind != 14 ) { @@ -1762,47 +4815,39 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 8 ) { keep = 8; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) + { + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 8 ) { kept = 8; r->report->clamped++; } + int32_t previous_count = value->entities_count; + value->entities_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_entities; } table_entity_load_body( &elem, &value->entities[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; table_entity_reset(&value->entities[i]); } + value->entities_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_entities: ; + { uint32_t tail; for (tail=(uint32_t)value->entities_count;tail<(uint32_t)previous_count && tail<8;tail++) { + table_entity_reset(&value->entities[tail]); + } } } - value->entities_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x76dd: /* stats */ + case 0xee639cad45b1994cull: /* stats */ { if ( kind != 14 ) { @@ -1810,47 +4855,39 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 80 ) { keep = 80; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) + { + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 80 ) { kept = 80; r->report->clamped++; } + int32_t previous_count = value->stats_count; + value->stats_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_stats; } table_stat_load_body( &elem, &value->stats[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; table_stat_reset(&value->stats[i]); } + value->stats_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_stats: ; + { uint32_t tail; for (tail=(uint32_t)value->stats_count;tail<(uint32_t)previous_count && tail<80;tail++) { + table_stat_reset(&value->stats[tail]); + } } } - value->stats_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0xa17e: /* game_event */ + case 0x2e35dc5321aa5790ull: /* game_event */ { if ( kind != 15 ) { @@ -1858,45 +4895,10 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint16_t arm_id; - uint32_t body_len; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - arm_id = table_reader_get16( r ); - if ( arm_id == 0 ) { value->game_event.type = TABLE_EVENT_TYPE_NONE; break; } /* empty: the id is the whole payload */ - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - switch ( arm_id ) /* the arm's NAME hash (docs/SPEC-TABLES.md §5) */ - { - case 0xba78: /* hit */ - value->game_event.type = TABLE_EVENT_TYPE_HIT; - table_hit_event_load_body( &sub, &value->game_event.as.hit ); - break; - case 0x5be0: /* chat */ - value->game_event.type = TABLE_EVENT_TYPE_CHAT; - table_chat_event_load_body( &sub, &value->game_event.as.chat ); - break; - case 0x99dd: /* pickup */ - value->game_event.type = TABLE_EVENT_TYPE_PICKUP; - table_pickup_event_load_body( &sub, &value->game_event.as.pickup ); - break; - default: - /* an arm this reader cannot name: the value reads EMPTY and - the body is skipped by its length, never misdecoded. The - reset is explicit, not the prefill's: a repeated field id - must not leave an arm decoded by an earlier occurrence - standing (docs/SPEC-TABLES.md §4). */ - value->game_event.type = TABLE_EVENT_TYPE_NONE; - r->report->unknown++; - break; - } - } - r->offset += body_len; + if ( !schema_benchtable_table_event_wire_load_( r, &value->game_event, 0 ) ) { return 0; } break; } - case 0x9f78: /* loadout */ + case 0x5759ce7586bbb5a3ull: /* loadout */ { if ( kind != 14 ) { @@ -1904,40 +4906,42 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - if ( elem_kind != 6 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; break; } - { - uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); - value->loadout[i] = decoded_v; - } - } - } - r->offset = body_end; /* excess elements and slack skip via the length */ + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 6 ) + { + if ( !(table_kind_widens(elem_kind,6)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 6: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_loadout; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); + value->loadout[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_loadout; + } + } + end_loadout: ; + } + } break; } - case 0x2d3e: /* player_name */ + case 0x13a62f542cf8e86eull: /* player_name */ { if ( kind != 12 ) { @@ -1945,20 +4949,18 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 15 ) { keep = 15; r->report->clamped++; } - memcpy( value->player_name, r->buffer + r->offset, keep ); - value->player_name[keep] = 0; - value->player_name_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->player_name[0] = 0; value->player_name_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 15 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 15 ); r->report->clamped++; } + memcpy( value->player_name, sub.buffer, (size_t) keep ); value->player_name[keep] = 0; value->player_name_length = (int32_t) keep; break; } - case 0x44aa: /* payload */ + case 0xcfb8a9d063b5e9e5ull: /* payload */ { if ( kind != 14 ) { @@ -1966,43 +4968,44 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 6 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 16 ) { keep = 16; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; break; } - { - uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); - value->payload[i] = decoded_v; - } - decoded = i + 1; - } - value->payload_length = (int32_t) decoded; - } - r->offset = body_end; /* excess elements and slack skip via the length */ + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 6 ) + { + if ( !(table_kind_widens(elem_kind,6)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 16 ) { kept = 16; r->report->clamped++; } + value->payload_length = 0; + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 6: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_payload; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); + value->payload[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_payload; + } + value->payload_length = (int32_t) i + 1; + } + end_payload: ; + } + } break; } - case 0x84e9: /* aim_x */ + case 0xdbaf7be5e24296c9ull: /* aim_x */ { if ( kind != 10 ) { @@ -2010,16 +5013,24 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); - if ( decoded_f < -1.0f ) { decoded_f = -1.0f; r->report->clamped++; } - else if ( decoded_f > 1.0f ) { decoded_f = 1.0f; r->report->clamped++; } - value->aim_x = decoded_f; + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); + if ( decoded_f < -1.0f ) { decoded_f = -1.0f; r->report->clamped++; } + else if ( decoded_f > 1.0f ) { decoded_f = 1.0f; r->report->clamped++; } + value->aim_x = decoded_f; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x8d96: /* aim_y */ + case 0xdbaf7ae5e2429516ull: /* aim_y */ { if ( kind != 10 ) { @@ -2027,16 +5038,24 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); - if ( decoded_f < -1.0f ) { decoded_f = -1.0f; r->report->clamped++; } - else if ( decoded_f > 1.0f ) { decoded_f = 1.0f; r->report->clamped++; } - value->aim_y = decoded_f; + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); + if ( decoded_f < -1.0f ) { decoded_f = -1.0f; r->report->clamped++; } + else if ( decoded_f > 1.0f ) { decoded_f = 1.0f; r->report->clamped++; } + value->aim_y = decoded_f; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x8e03: /* aim_z */ + case 0xdbaf79e5e2429363ull: /* aim_z */ { if ( kind != 10 ) { @@ -2044,16 +5063,24 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); - if ( decoded_f < -1.0f ) { decoded_f = -1.0f; r->report->clamped++; } - else if ( decoded_f > 1.0f ) { decoded_f = 1.0f; r->report->clamped++; } - value->aim_z = decoded_f; + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); + if ( decoded_f < -1.0f ) { decoded_f = -1.0f; r->report->clamped++; } + else if ( decoded_f > 1.0f ) { decoded_f = 1.0f; r->report->clamped++; } + value->aim_z = decoded_f; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x2d04: /* recoil */ + case 0x9cef31fff7e2e457ull: /* recoil */ { if ( kind != 10 ) { @@ -2061,55 +5088,250 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->recoil = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->recoil = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xc023: /* drift */ + case 0x5ab3f9c9341f6c04ull: /* drift */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->drift = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->drift = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->drift = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->drift = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->drift = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x272f: /* wide_key */ + case 0xa3348580461faf16ull: /* wide_key */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->wide_key = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->wide_key = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->wide_key = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->wide_key = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->wide_key = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->wide_key = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->wide_key = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->wide_key = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->wide_key = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x196a: /* flux */ + case 0xd61bdd7908af2642ull: /* flux */ { if ( kind != 5 ) { + if ( table_kind_widens( kind, 5 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -1000000000000000000ll ) { decoded_wide = -1000000000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000000000ll ) { decoded_wide = 1000000000000000000ll; r->report->clamped++; } + value->flux = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -1000000000000000000ll ) { decoded_wide = -1000000000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000000000ll ) { decoded_wide = 1000000000000000000ll; r->report->clamped++; } + value->flux = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < -1000000000000000000ll ) { decoded_wide = -1000000000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000000000ll ) { decoded_wide = 1000000000000000000ll; r->report->clamped++; } + value->flux = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v < -1000000000000000000ll ) { decoded_v = -1000000000000000000ll; r->report->clamped++; } + else if ( decoded_v > 1000000000000000000ll ) { decoded_v = 1000000000000000000ll; r->report->clamped++; } + value->flux = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); - if ( decoded_v < -1000000000000000000ll ) { decoded_v = -1000000000000000000ll; r->report->clamped++; } - else if ( decoded_v > 1000000000000000000ll ) { decoded_v = 1000000000000000000ll; r->report->clamped++; } - value->flux = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -1000000000000000000ll ) { decoded_wide = -1000000000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000000000ll ) { decoded_wide = 1000000000000000000ll; r->report->clamped++; } + value->flux = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -1000000000000000000ll ) { decoded_wide = -1000000000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000000000ll ) { decoded_wide = 1000000000000000000ll; r->report->clamped++; } + value->flux = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < -1000000000000000000ll ) { decoded_wide = -1000000000000000000ll; r->report->clamped++; } + if ( decoded_wide > 1000000000000000000ll ) { decoded_wide = 1000000000000000000ll; r->report->clamped++; } + value->flux = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v < -1000000000000000000ll ) { decoded_v = -1000000000000000000ll; r->report->clamped++; } + else if ( decoded_v > 1000000000000000000ll ) { decoded_v = 1000000000000000000ll; r->report->clamped++; } + value->flux = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe6d4: /* ping */ + case 0xbf30e00dc53307a9ull: /* ping */ { if ( kind != 10 ) { @@ -2117,32 +5339,99 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); - if ( decoded_f < 0.0f ) { decoded_f = 0.0f; r->report->clamped++; } - else if ( decoded_f > 250.0f ) { decoded_f = 250.0f; r->report->clamped++; } - value->ping = decoded_f; + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + float decoded_f = table_bits_to_float( table_reader_get32( &(*r) ) ); + if ( decoded_f < 0.0f ) { decoded_f = 0.0f; r->report->clamped++; } + else if ( decoded_f > 250.0f ) { decoded_f = 250.0f; r->report->clamped++; } + value->ping = decoded_f; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xd3dc: /* crc_hint */ + case 0x560d6527ccd8515full: /* crc_hint */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 16777215ull ) { decoded_wide = 16777215ull; r->report->clamped++; } + value->crc_hint = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 16777215ull ) { decoded_wide = 16777215ull; r->report->clamped++; } + value->crc_hint = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v > 16777215ull ) { decoded_v = 16777215ull; r->report->clamped++; } /* bits(24) width clamp */ + value->crc_hint = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - if ( decoded_v > 16777215ull ) { decoded_v = 16777215ull; r->report->clamped++; } /* bits(24) width clamp */ - value->crc_hint = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 16777215ull ) { decoded_wide = 16777215ull; r->report->clamped++; } + value->crc_hint = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 16777215ull ) { decoded_wide = 16777215ull; r->report->clamped++; } + value->crc_hint = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v > 16777215ull ) { decoded_v = 16777215ull; r->report->clamped++; } /* bits(24) width clamp */ + value->crc_hint = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xb023: /* has_extra */ + case 0xc08292176cfd8672ull: /* has_extra */ { if ( kind != 1 ) { @@ -2150,171 +5439,1395 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_mixed_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->has_extra = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->has_extra = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xb579: /* extra */ + case 0xfd29ee12a979cb69ull: /* extra */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 255ll ) { decoded_wide = 255ll; r->report->clamped++; } + value->extra = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 255ll ) { decoded_wide = 255ll; r->report->clamped++; } + value->extra = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 255 ) { decoded_v = 255; r->report->clamped++; } + value->extra = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 255 ) { decoded_v = 255; r->report->clamped++; } - value->extra = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 255ll ) { decoded_wide = 255ll; r->report->clamped++; } + value->extra = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 255ll ) { decoded_wide = 255ll; r->report->clamped++; } + value->extra = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 255 ) { decoded_v = 255; r->report->clamped++; } + value->extra = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x9555: /* idle_ticks */ + case 0x78101ac0aa8cbcfeull: /* idle_ticks */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 15ll ) { decoded_wide = 15ll; r->report->clamped++; } + value->idle_ticks = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 15ll ) { decoded_wide = 15ll; r->report->clamped++; } + value->idle_ticks = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 15 ) { decoded_v = 15; r->report->clamped++; } + value->idle_ticks = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 15 ) { decoded_v = 15; r->report->clamped++; } - value->idle_ticks = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 15ll ) { decoded_wide = 15ll; r->report->clamped++; } + value->idle_ticks = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 15ll ) { decoded_wide = 15ll; r->report->clamped++; } + value->idle_ticks = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 15 ) { decoded_v = 15; r->report->clamped++; } + value->idle_ticks = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int table_mixed_load( TableMixed * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + table_mixed_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return table_mixed_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t table_hit_event_measure( const TableHitEvent * value ) +static SCHEMA_UNUSED int table_mixed_save_message_body(TableBitWriter * w,const TableMixed * value) { - int64_t bytes = 2; /* terminator */ - if ( value->target_id != 0 ) { bytes += 3 + 2; } /* target_id */ - if ( value->damage != 0 ) { bytes += 3 + 4; } /* damage */ - if ( value->hit_kind != 0 ) { bytes += 3 + 4; } /* hit_kind */ - if ( value->crit != 0 ) { bytes += 3 + 1; } /* crit */ - return bytes; + (void)value; + { /* protocol_magic */ + if(!(value->protocol_magic == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,21,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->protocol_magic)-UINT64_C(0),16); + } + } + { /* sequence */ + if(!(value->sequence == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,22,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->sequence)-UINT64_C(0),16); + } + } + { /* ack_sequence */ + if(!(value->ack_sequence == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,23,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->ack_sequence)-UINT64_C(0),16); + } + } + { /* ack_bits */ + if(!(value->ack_bits == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->ack_bits)-UINT64_C(0),32); + } + } + { /* session_id */ + if(!(value->session_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,25,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->session_id)-UINT64_C(0),64); + } + } + { /* client_id */ + if(!(value->client_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,26,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->client_id)-UINT64_C(0),32); + } + } + { /* nonce */ + if(!(value->nonce == 1ull)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,27,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->nonce)-UINT64_C(1),63); + } + } + { /* world_time */ + if(!(value->world_time == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,28,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->world_time)-UINT64_C(18446743073709551616),41); + } + } + { /* frame_tick */ + if(!(value->frame_tick == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,29,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->frame_tick)-UINT64_C(0),48); + } + } + { /* server_time */ + if(!(value->server_time == 0.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,30,kTableMessageRefBitsHere); + table_bit_put(w,table_message_quantize(value->server_time,0.0f,65535.0f,6553500u),23); + } + } + { /* entities */ + if(value->entities_count<0 || value->entities_count>8) return 0; + if(value->entities_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,31,kTableMessageRefBitsHere); + if(value->entities_count<0 || value->entities_count>8) return 0; + table_bit_put(w,(uint64_t)value->entities_count-1,3); + { int32_t i; + for(i=0;ientities_count;i++) { + if(!table_entity_save_message_body(w,&value->entities[i])) return 0; + } } + } + } + { /* stats */ + if(value->stats_count<0 || value->stats_count>80) return 0; + if(value->stats_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,32,kTableMessageRefBitsHere); + if(value->stats_count<0 || value->stats_count>80) return 0; + table_bit_put(w,(uint64_t)value->stats_count-0,7); + { int32_t i; + for(i=0;istats_count;i++) { + if(!table_stat_save_message_body(w,&value->stats[i])) return 0; + } } + } + } + { /* game_event */ + if(value->game_event.type!=TABLE_EVENT_TYPE_NONE) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,33,kTableMessageRefBitsHere); + if(!schema_benchtable_table_event_wire_message_save_(w,&value->game_event)) return 0; + } + } + { /* loadout */ + int rides=0; + { int32_t i; + for(i=0;i<4;i++) if(!(value->loadout[i] == 0)) { rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,34,kTableMessageRefBitsHere); + if(4<0 || 4>4) return 0; + table_bit_put(w,(uint64_t)4-4,0); + table_bit_align_write(w); + { int32_t i; + for(i=0;i<4;i++) { + table_bit_put(w,(uint64_t)(value->loadout[i])-UINT64_C(0),8); + } } + } + } + { /* player_name */ + if(value->player_name_length<0 || value->player_name_length>15) return 0; + if(value->player_name_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,35,kTableMessageRefBitsHere); + if(value->player_name_length<0 || value->player_name_length>15) return 0; + table_bit_put(w,(uint64_t)value->player_name_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->player_name,value->player_name_length); + } + } + { /* payload */ + if(value->payload_length<0 || value->payload_length>16) return 0; + if(value->payload_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,36,kTableMessageRefBitsHere); + if(value->payload_length<0 || value->payload_length>16) return 0; + table_bit_put(w,(uint64_t)value->payload_length,5); + table_bit_align_write(w); + table_bit_putbytes(w,value->payload,value->payload_length); + } + } + { /* aim_x */ + if(!(value->aim_x == 0.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,37,kTableMessageRefBitsHere); + table_bit_put(w,table_message_quantize(value->aim_x,-1.0f,2.0f,200u),8); + } + } + { /* aim_y */ + if(!(value->aim_y == 0.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,38,kTableMessageRefBitsHere); + table_bit_put(w,table_message_quantize(value->aim_y,-1.0f,2.0f,200u),8); + } + } + { /* aim_z */ + if(!(value->aim_z == 0.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,39,kTableMessageRefBitsHere); + table_bit_put(w,table_message_quantize(value->aim_z,-1.0f,2.0f,200u),8); + } + } + { /* recoil */ + if(!(value->recoil == 0.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,40,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->recoil),32); + } + } + { /* drift */ + if(!(value->drift == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,41,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->drift),64); + } + } + { /* wide_key */ + if(!(value->wide_key == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,42,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->wide_key)-UINT64_C(0),64); + } + } + { /* flux */ + if(!(value->flux == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,43,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->flux)-UINT64_C(17446744073709551616),61); + } + } + { /* ping */ + if(!(value->ping == 0.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,44,kTableMessageRefBitsHere); + table_bit_put(w,table_message_quantize(value->ping,0.0f,250.0f,25000u),15); + } + } + { /* crc_hint */ + if(!(value->crc_hint == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,45,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->crc_hint)-UINT64_C(0),24); + } + } + { /* has_extra */ + if(!(value->has_extra == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,46,kTableMessageRefBitsHere); + table_bit_put(w,value->has_extra ? 1 : 0,1); + } + } + { /* extra */ + if(value->has_extra) { + if(!(value->extra == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,47,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->extra)-UINT64_C(0),8); + } + } + } + { /* idle_ticks */ + if(!value->has_extra) { + if(!(value->idle_ticks == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,48,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->idle_ticks)-UINT64_C(0),4); + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_hit_event_save_body( TableWriter * w, const TableHitEvent * value ) +static SCHEMA_UNUSED int64_t table_mixed_measure_messages(const TableMixed * values,int64_t count,TableReport * report) { - if ( value->target_id != 0 ) - { - table_writer_put16( w, 0xdf6a ); table_writer_put8( w, 7 ); /* target_id */ - table_writer_put16( w, (uint16_t) ( value->target_id ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x6a5a70d91aa115fd): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>65535ull){decoded=65535ull; + r->report->clamped++; + } + value->protocol_magic=(uint16_t)decoded; + } + break; + } + case UINT64_C(0xaa38aca481f528a8): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>65535ull){decoded=65535ull; + r->report->clamped++; + } + value->sequence=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x0dbe005c56697c3e): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>65535ll){decoded=65535ll; + r->report->clamped++; + } + value->ack_sequence=(int32_t)decoded; + } + break; + } + case UINT64_C(0x9bae0da8b829ee03): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->ack_bits=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xb7d7b5650a590b05): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->session_id=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x6d7b98e2d095967e): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->client_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x73a94c71d60dc0d8): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>9223372036854775807ull){decoded=9223372036854775807ull; + r->report->clamped++; + } + value->nonce=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x3eee6b51be54fc85): { + if(!(entry->kind==5 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,5)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-1000000000000ll){decoded=-1000000000000ll; + r->report->clamped++; + } + if(decoded>1000000000000ll){decoded=1000000000000ll; + r->report->clamped++; + } + value->world_time=(int64_t)decoded; + } + break; + } + case UINT64_C(0x7bbc035f7b6d0112): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>281474976710655ull){decoded=281474976710655ull; + r->report->clamped++; + } + value->frame_tick=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x3c460475f9be69c6): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + if(decoded<0.0f){decoded=0.0f; + r->report->clamped++; + }else if(decoded>65535.0f){decoded=65535.0f; + r->report->clamped++; + } + value->server_time=decoded; + } + break; + } + case UINT64_C(0x935d0fb07bb3822a): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>8?8:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ientities[i] : &scratch; + if(!table_entity_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->entities_count=(int32_t)kept; + } + break; + } + case UINT64_C(0xee639cad45b1994c): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>80?80:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;istats[i] : &scratch; + if(!table_stat_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->stats_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x2e35dc5321aa5790): { + if(!(entry->kind==15 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,15)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!schema_benchtable_table_event_wire_message_load_(r,&value->game_event))return 0; + break; + } + case UINT64_C(0x5759ce7586bbb5a3): { + if(!(entry->kind==14 && entry->elem_kind==6)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iloadout[i] : &scratch; + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + (*item)=(uint8_t)decoded; + } + }if(walkvalue_bits))goto malformed; + } + break; + } + case UINT64_C(0x13a62f542cf8e86e): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,15); + memcpy(value->player_name,text,(size_t)kept); + value->player_name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->player_name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xcfb8a9d063b5e9e5): { + if(!(entry->kind==14 && entry->elem_kind==6)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,14)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(entry->min,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + kept=n>16 ? 16 : n; + if(!table_bit_getbytes(&r->bits,value->payload,(int64_t)kept)||!table_message_skip_run(r,n-kept,8))goto malformed; + if(keptreport->clamped++; + value->payload_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xdbaf7be5e24296c9): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + if(decoded<-1.0f){decoded=-1.0f; + r->report->clamped++; + }else if(decoded>1.0f){decoded=1.0f; + r->report->clamped++; + } + value->aim_x=decoded; + } + break; + } + case UINT64_C(0xdbaf7ae5e2429516): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + if(decoded<-1.0f){decoded=-1.0f; + r->report->clamped++; + }else if(decoded>1.0f){decoded=1.0f; + r->report->clamped++; + } + value->aim_y=decoded; + } + break; + } + case UINT64_C(0xdbaf79e5e2429363): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + if(decoded<-1.0f){decoded=-1.0f; + r->report->clamped++; + }else if(decoded>1.0f){decoded=1.0f; + r->report->clamped++; + } + value->aim_z=decoded; + } + break; + } + case UINT64_C(0x9cef31fff7e2e457): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->recoil=decoded; + } + break; + } + case UINT64_C(0x5ab3f9c9341f6c04): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->drift=decoded; + } + break; + } + case UINT64_C(0xa3348580461faf16): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->wide_key=(uint64_t)decoded; + } + break; + } + case UINT64_C(0xd61bdd7908af2642): { + if(!(entry->kind==5 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,5)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-1000000000000000000ll){decoded=-1000000000000000000ll; + r->report->clamped++; + } + if(decoded>1000000000000000000ll){decoded=1000000000000000000ll; + r->report->clamped++; + } + value->flux=(int64_t)decoded; + } + break; + } + case UINT64_C(0xbf30e00dc53307a9): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + if(decoded<0.0f){decoded=0.0f; + r->report->clamped++; + }else if(decoded>250.0f){decoded=250.0f; + r->report->clamped++; + } + value->ping=decoded; + } + break; + } + case UINT64_C(0x560d6527ccd8515f): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>16777215ull){decoded=16777215ull; + r->report->clamped++; + } + value->crc_hint=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xc08292176cfd8672): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->has_extra=lo!=0; + } + break; + } + case UINT64_C(0xfd29ee12a979cb69): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>255ll){decoded=255ll; + r->report->clamped++; + } + value->extra=(int32_t)decoded; + } + break; + } + case UINT64_C(0x78101ac0aa8cbcfe): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>15ll){decoded=15ll; + r->report->clamped++; + } + value->idle_ticks=(int32_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int table_mixed_load_messages(TableMixed * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_hit_event_save_body( TableWriter * w, const TableHitEvent * value ) +{ + (void) value; + { /* target_id */ + if ( !( value->target_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb7bc9ac015a25050ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->target_id ) ); + } } - if ( value->damage != 0 ) - { - table_writer_put16( w, 0x15a9 ); table_writer_put8( w, 4 ); /* damage */ - table_writer_put32( w, (uint32_t) ( value->damage ) ); + { /* damage */ + if ( !( value->damage == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7f6308be8ab37fc0ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->damage ) ); + } } - if ( value->hit_kind != 0 ) - { - table_writer_put16( w, 0xaf83 ); table_writer_put8( w, 4 ); /* hit_kind */ - table_writer_put32( w, (uint32_t) ( value->hit_kind ) ); + { /* hit_kind */ + if ( !( value->hit_kind == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x01fbc365b059b925ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->hit_kind ) ); + } } - if ( value->crit != 0 ) - { - table_writer_put16( w, 0x93d9 ); table_writer_put8( w, 1 ); /* crit */ - table_writer_put8( w, value->crit ? 1 : 0 ); + { /* crit */ + if ( !( value->crit == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x126167908c9aa52dull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->crit ? 1 : 0 ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t table_hit_event_measure( const TableHitEvent * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !table_hit_event_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t table_hit_event_save( const TableHitEvent * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !table_hit_event_save_body( &w, value ) ) { return -1; } - return w.offset; /* == table_hit_event_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_hit_event_load_body( TableReader * r, TableHitEvent * value ) +static SCHEMA_UNUSED int table_hit_event_load_body( TableReader * r, TableHitEvent * value ) { - table_hit_event_reset( value ); /* prefill declared defaults in place, then overlay */ + table_hit_event_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xdf6a: /* target_id */ + case 0xb7bc9ac015a25050ull: /* target_id */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4095ull ) { decoded_wide = 4095ull; r->report->clamped++; } + value->target_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ + value->target_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ - value->target_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4095ull ) { decoded_wide = 4095ull; r->report->clamped++; } + value->target_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ + value->target_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x15a9: /* damage */ + case 0x7f6308be8ab37fc0ull: /* damage */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 4095ll ) { decoded_wide = 4095ll; r->report->clamped++; } + value->damage = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 4095ll ) { decoded_wide = 4095ll; r->report->clamped++; } + value->damage = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 4095 ) { decoded_v = 4095; r->report->clamped++; } + value->damage = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 4095 ) { decoded_v = 4095; r->report->clamped++; } - value->damage = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 4095ll ) { decoded_wide = 4095ll; r->report->clamped++; } + value->damage = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 4095ll ) { decoded_wide = 4095ll; r->report->clamped++; } + value->damage = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 4095 ) { decoded_v = 4095; r->report->clamped++; } + value->damage = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xaf83: /* hit_kind */ + case 0x01fbc365b059b925ull: /* hit_kind */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 7ll ) { decoded_wide = 7ll; r->report->clamped++; } + value->hit_kind = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 7ll ) { decoded_wide = 7ll; r->report->clamped++; } + value->hit_kind = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 7 ) { decoded_v = 7; r->report->clamped++; } + value->hit_kind = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 7 ) { decoded_v = 7; r->report->clamped++; } - value->hit_kind = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 7ll ) { decoded_wide = 7ll; r->report->clamped++; } + value->hit_kind = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 7ll ) { decoded_wide = 7ll; r->report->clamped++; } + value->hit_kind = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 7 ) { decoded_v = 7; r->report->clamped++; } + value->hit_kind = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x93d9: /* crit */ + case 0x126167908c9aa52dull: /* crit */ { if ( kind != 1 ) { @@ -2322,223 +6835,986 @@ static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_hit_event_load_bod if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->crit = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->crit = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int table_hit_event_load( TableHitEvent * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + table_hit_event_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return table_hit_event_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t table_chat_event_measure( const TableChatEvent * value ) +static SCHEMA_UNUSED int table_hit_event_save_message_body(TableBitWriter * w,const TableHitEvent * value) { - int64_t bytes = 2; /* terminator */ - if ( value->channel != 0 ) { bytes += 3 + 4; } /* channel */ - if ( value->speaker != 0 ) { bytes += 3 + 2; } /* speaker */ - return bytes; + (void)value; + { /* target_id */ + if(!(value->target_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,17,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->target_id)-UINT64_C(0),12); + } + } + { /* damage */ + if(!(value->damage == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,18,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->damage)-UINT64_C(0),12); + } + } + { /* hit_kind */ + if(!(value->hit_kind == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,19,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->hit_kind)-UINT64_C(0),3); + } + } + { /* crit */ + if(!(value->crit == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,20,kTableMessageRefBitsHere); + table_bit_put(w,value->crit ? 1 : 0,1); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_chat_event_save_body( TableWriter * w, const TableChatEvent * value ) +static SCHEMA_UNUSED int64_t table_hit_event_measure_messages(const TableHitEvent * values,int64_t count,TableReport * report) { - if ( value->channel != 0 ) - { - table_writer_put16( w, 0x7366 ); table_writer_put8( w, 4 ); /* channel */ - table_writer_put32( w, (uint32_t) ( value->channel ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xb7bc9ac015a25050): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4095ull){decoded=4095ull; + r->report->clamped++; + } + value->target_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x7f6308be8ab37fc0): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>4095ll){decoded=4095ll; + r->report->clamped++; + } + value->damage=(int32_t)decoded; + } + break; + } + case UINT64_C(0x01fbc365b059b925): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>7ll){decoded=7ll; + r->report->clamped++; + } + value->hit_kind=(int32_t)decoded; + } + break; + } + case UINT64_C(0x126167908c9aa52d): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->crit=lo!=0; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int table_hit_event_load_messages(TableHitEvent * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_chat_event_save_body( TableWriter * w, const TableChatEvent * value ) +{ + (void) value; + { /* channel */ + if ( !( value->channel == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa5013e9ad5caeda4ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->channel ) ); + } } - if ( value->speaker != 0 ) - { - table_writer_put16( w, 0xce0b ); table_writer_put8( w, 7 ); /* speaker */ - table_writer_put16( w, (uint16_t) ( value->speaker ) ); + { /* speaker */ + if ( !( value->speaker == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfbf1ac4d96ebd022ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->speaker ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t table_chat_event_measure( const TableChatEvent * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !table_chat_event_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t table_chat_event_save( const TableChatEvent * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !table_chat_event_save_body( &w, value ) ) { return -1; } - return w.offset; /* == table_chat_event_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_chat_event_load_body( TableReader * r, TableChatEvent * value ) +static SCHEMA_UNUSED int table_chat_event_load_body( TableReader * r, TableChatEvent * value ) { - table_chat_event_reset( value ); /* prefill declared defaults in place, then overlay */ + table_chat_event_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x7366: /* channel */ + case 0xa5013e9ad5caeda4ull: /* channel */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 3ll ) { decoded_wide = 3ll; r->report->clamped++; } + value->channel = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 3ll ) { decoded_wide = 3ll; r->report->clamped++; } + value->channel = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 3 ) { decoded_v = 3; r->report->clamped++; } + value->channel = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 3 ) { decoded_v = 3; r->report->clamped++; } - value->channel = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 3ll ) { decoded_wide = 3ll; r->report->clamped++; } + value->channel = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 3ll ) { decoded_wide = 3ll; r->report->clamped++; } + value->channel = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 3 ) { decoded_v = 3; r->report->clamped++; } + value->channel = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xce0b: /* speaker */ + case 0xfbf1ac4d96ebd022ull: /* speaker */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4095ull ) { decoded_wide = 4095ull; r->report->clamped++; } + value->speaker = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ + value->speaker = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ - value->speaker = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4095ull ) { decoded_wide = 4095ull; r->report->clamped++; } + value->speaker = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ + value->speaker = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int table_chat_event_load( TableChatEvent * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + table_chat_event_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return table_chat_event_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t table_pickup_event_measure( const TablePickupEvent * value ) +static SCHEMA_UNUSED int table_chat_event_save_message_body(TableBitWriter * w,const TableChatEvent * value) { - int64_t bytes = 2; /* terminator */ - if ( value->item_id != 0 ) { bytes += 3 + 2; } /* item_id */ - if ( value->amount != 0 ) { bytes += 3 + 4; } /* amount */ - return bytes; + (void)value; + { /* channel */ + if(!(value->channel == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,1,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->channel)-UINT64_C(0),2); + } + } + { /* speaker */ + if(!(value->speaker == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,2,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->speaker)-UINT64_C(0),12); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_pickup_event_save_body( TableWriter * w, const TablePickupEvent * value ) +static SCHEMA_UNUSED int64_t table_chat_event_measure_messages(const TableChatEvent * values,int64_t count,TableReport * report) { - if ( value->item_id != 0 ) - { - table_writer_put16( w, 0xec67 ); table_writer_put8( w, 7 ); /* item_id */ - table_writer_put16( w, (uint16_t) ( value->item_id ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xa5013e9ad5caeda4): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>3ll){decoded=3ll; + r->report->clamped++; + } + value->channel=(int32_t)decoded; + } + break; + } + case UINT64_C(0xfbf1ac4d96ebd022): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4095ull){decoded=4095ull; + r->report->clamped++; + } + value->speaker=(uint32_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int table_chat_event_load_messages(TableChatEvent * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_pickup_event_save_body( TableWriter * w, const TablePickupEvent * value ) +{ + (void) value; + { /* item_id */ + if ( !( value->item_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x9e7fd06d864fbd56ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->item_id ) ); + } } - if ( value->amount != 0 ) - { - table_writer_put16( w, 0x39cc ); table_writer_put8( w, 4 ); /* amount */ - table_writer_put32( w, (uint32_t) ( value->amount ) ); + { /* amount */ + if ( !( value->amount == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x8113fe7ea2b16969ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->amount ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t table_pickup_event_measure( const TablePickupEvent * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !table_pickup_event_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t table_pickup_event_save( const TablePickupEvent * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !table_pickup_event_save_body( &w, value ) ) { return -1; } - return w.offset; /* == table_pickup_event_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BENCHTABLE_TABLE_INLINE int table_pickup_event_load_body( TableReader * r, TablePickupEvent * value ) +static SCHEMA_UNUSED int table_pickup_event_load_body( TableReader * r, TablePickupEvent * value ) { - table_pickup_event_reset( value ); /* prefill declared defaults in place, then overlay */ + table_pickup_event_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xec67: /* item_id */ + case 0x9e7fd06d864fbd56ull: /* item_id */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 1023ull ) { decoded_wide = 1023ull; r->report->clamped++; } + value->item_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 1023ull ) { decoded_v = 1023ull; r->report->clamped++; } /* bits(10) width clamp */ + value->item_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 1023ull ) { decoded_v = 1023ull; r->report->clamped++; } /* bits(10) width clamp */ - value->item_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 1023ull ) { decoded_wide = 1023ull; r->report->clamped++; } + value->item_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 1023ull ) { decoded_v = 1023ull; r->report->clamped++; } /* bits(10) width clamp */ + value->item_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x39cc: /* amount */ + case 0x8113fe7ea2b16969ull: /* amount */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 255ll ) { decoded_wide = 255ll; r->report->clamped++; } + value->amount = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 255ll ) { decoded_wide = 255ll; r->report->clamped++; } + value->amount = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 255 ) { decoded_v = 255; r->report->clamped++; } + value->amount = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 255 ) { decoded_v = 255; r->report->clamped++; } - value->amount = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 255ll ) { decoded_wide = 255ll; r->report->clamped++; } + value->amount = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 255ll ) { decoded_wide = 255ll; r->report->clamped++; } + value->amount = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 255 ) { decoded_v = 255; r->report->clamped++; } + value->amount = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int table_pickup_event_load( TablePickupEvent * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + table_pickup_event_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return table_pickup_event_load_body( &r, value ); } +static SCHEMA_UNUSED int table_pickup_event_save_message_body(TableBitWriter * w,const TablePickupEvent * value) +{ + (void)value; + { /* item_id */ + if(!(value->item_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,49,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->item_id)-UINT64_C(0),10); + } + } + { /* amount */ + if(!(value->amount == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,50,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->amount)-UINT64_C(0),8); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t table_pickup_event_measure_messages(const TablePickupEvent * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x9e7fd06d864fbd56): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>1023ull){decoded=1023ull; + r->report->clamped++; + } + value->item_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x8113fe7ea2b16969): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>255ll){decoded=255ll; + r->report->clamped++; + } + value->amount=(int32_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int table_pickup_event_load_messages(TablePickupEvent * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_entity_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_entity_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_entity_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_entity_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define table_entity_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_stat_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_stat_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_stat_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_stat_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define table_stat_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_mixed_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_mixed_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_mixed_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_mixed_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define table_mixed_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_hit_event_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_hit_event_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_hit_event_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_hit_event_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define table_hit_event_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_chat_event_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_chat_event_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_chat_event_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_chat_event_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define table_chat_event_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_pickup_event_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_pickup_event_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_pickup_event_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define table_pickup_event_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define table_pickup_event_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* table_entity_open: match the header and POINT. On a match the bytes ARE what this @@ -2560,6 +7836,8 @@ static SCHEMA_UNUSED int table_pickup_event_load( TablePickupEvent * value, cons file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const TableEntity * table_entity_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const TableEntity *)table_cook_open_ex(bytes,length,sizeof(TableEntity),SCHEMA_TABLE_ALIGNOF(TableEntity),reason); } static SCHEMA_UNUSED const TableEntity * table_entity_open( const void * bytes, uint64_t length ) { return (const TableEntity *) table_cook_open( bytes, length, (uint64_t) sizeof( TableEntity ), (uint64_t) SCHEMA_TABLE_ALIGNOF( TableEntity ) ); @@ -2584,6 +7862,8 @@ static SCHEMA_UNUSED const TableEntity * table_entity_open( const void * bytes, file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const TableStat * table_stat_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const TableStat *)table_cook_open_ex(bytes,length,sizeof(TableStat),SCHEMA_TABLE_ALIGNOF(TableStat),reason); } static SCHEMA_UNUSED const TableStat * table_stat_open( const void * bytes, uint64_t length ) { return (const TableStat *) table_cook_open( bytes, length, (uint64_t) sizeof( TableStat ), (uint64_t) SCHEMA_TABLE_ALIGNOF( TableStat ) ); @@ -2608,11 +7888,153 @@ static SCHEMA_UNUSED const TableStat * table_stat_open( const void * bytes, uint file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const TableMixed * table_mixed_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const TableMixed *)table_cook_open_ex(bytes,length,sizeof(TableMixed),SCHEMA_TABLE_ALIGNOF(TableMixed),reason); } static SCHEMA_UNUSED const TableMixed * table_mixed_open( const void * bytes, uint64_t length ) { return (const TableMixed *) table_cook_open( bytes, length, (uint64_t) sizeof( TableMixed ), (uint64_t) SCHEMA_TABLE_ALIGNOF( TableMixed ) ); } +static SCHEMA_UNUSED int schema_benchtable_table_entity_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TableEntity * value=(const TableEntity *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->entity_id,4,order); + table_cook_put(at+4,(uint64_t)value->pos_x,4,order); + table_cook_put(at+8,(uint64_t)value->pos_y,4,order); + table_cook_put(at+12,(uint64_t)value->pos_z,4,order); + table_cook_put(at+16,(uint64_t)value->yaw,4,order); + table_cook_put(at+20,(uint64_t)value->pitch,4,order); + table_cook_put(at+24,(uint64_t)value->vel_x,4,order); + table_cook_put(at+28,(uint64_t)value->vel_y,4,order); + table_cook_put(at+32,(uint64_t)value->vel_z,4,order); + table_cook_put(at+36,(uint64_t)value->health,4,order); + table_cook_put(at+40,(uint64_t)value->weapon,1,order); + table_cook_put(at+48,(uint64_t)value->damage,8,order); + table_cook_put(at+56,value->moving ? 1 : 0,1,order); + table_cook_put(at+57,value->firing ? 1 : 0,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t table_entity_cook_measure(const TableEntity * value) { (void)value; return 144; } +static SCHEMA_UNUSED int table_entity_cook(const TableEntity * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<144) { return 0; } + memset(raw,0,144); + if(!schema_benchtable_table_entity_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+136,UINT64_C(0x3161723596c6cba8),8,order); + table_cook_header(raw,SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE,64,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_benchtable_table_stat_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TableStat * value=(const TableStat *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->stat_id,4,order); + table_cook_put(at+4,(uint64_t)value->delta,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t table_stat_cook_measure(const TableStat * value) { (void)value; return 88; } +static SCHEMA_UNUSED int table_stat_cook(const TableStat * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<88) { return 0; } + memset(raw,0,88); + if(!schema_benchtable_table_stat_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+80,UINT64_C(0x296aa8e669b99c95),8,order); + table_cook_header(raw,SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE,8,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_benchtable_table_mixed_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TableMixed * value=(const TableMixed *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->protocol_magic,2,order); + table_cook_put(at+4,(uint64_t)value->sequence,4,order); + table_cook_put(at+8,(uint64_t)value->ack_sequence,4,order); + table_cook_put(at+12,(uint64_t)value->ack_bits,4,order); + table_cook_put(at+16,(uint64_t)value->session_id,8,order); + table_cook_put(at+24,(uint64_t)value->client_id,4,order); + table_cook_put(at+32,(uint64_t)value->nonce,8,order); + table_cook_put(at+40,(uint64_t)value->world_time,8,order); + table_cook_put(at+48,(uint64_t)value->frame_tick,8,order); + { uint32_t bits; memcpy(&bits,&value->server_time,4); table_cook_put(at+56,bits,4,order); } + { int32_t cook_i1; for(cook_i1=0;cook_i1<8;cook_i1++) { + if(!schema_benchtable_table_entity_cook_body_(n,at+64+cook_i1*64,&value->entities[cook_i1],order)) { return 0; } + } } + table_cook_put(at+64+512,(uint32_t)value->entities_count,4,order); + { int32_t cook_i2; for(cook_i2=0;cook_i2<80;cook_i2++) { + if(!schema_benchtable_table_stat_cook_body_(n,at+580+cook_i2*8,&value->stats[cook_i2],order)) { return 0; } + } } + table_cook_put(at+580+640,(uint32_t)value->stats_count,4,order); + table_cook_put(at+1224,(uint64_t)value->game_event.type,1,order); + switch(value->game_event.type) { + case TABLE_EVENT_TYPE_HIT: { + if(!schema_benchtable_table_hit_event_cook_body_(n,at+1224+4,&value->game_event.as.hit,order)) { return 0; } + break; } + case TABLE_EVENT_TYPE_CHAT: { + if(!schema_benchtable_table_chat_event_cook_body_(n,at+1224+4,&value->game_event.as.chat,order)) { return 0; } + break; } + case TABLE_EVENT_TYPE_PICKUP: { + if(!schema_benchtable_table_pickup_event_cook_body_(n,at+1224+4,&value->game_event.as.pickup,order)) { return 0; } + break; } + default: break; + } + { int32_t cook_i3; for(cook_i3=0;cook_i3<4;cook_i3++) { + table_cook_put(at+1244+cook_i3*1,(uint64_t)value->loadout[cook_i3],1,order); + } } + table_cook_bytes(at+1248,value->player_name,value->player_name_length,16); + table_cook_put(at+1248+16,(uint32_t)value->player_name_length,4,order); + table_cook_bytes(at+1268,value->payload,value->payload_length,16); + table_cook_put(at+1268+16,(uint32_t)value->payload_length,4,order); + { uint32_t bits; memcpy(&bits,&value->aim_x,4); table_cook_put(at+1288,bits,4,order); } + { uint32_t bits; memcpy(&bits,&value->aim_y,4); table_cook_put(at+1292,bits,4,order); } + { uint32_t bits; memcpy(&bits,&value->aim_z,4); table_cook_put(at+1296,bits,4,order); } + { uint32_t bits; memcpy(&bits,&value->recoil,4); table_cook_put(at+1300,bits,4,order); } + { uint64_t bits; memcpy(&bits,&value->drift,8); table_cook_put(at+1304,bits,8,order); } + table_cook_put(at+1312,(uint64_t)value->wide_key,8,order); + table_cook_put(at+1320,(uint64_t)value->flux,8,order); + { uint32_t bits; memcpy(&bits,&value->ping,4); table_cook_put(at+1328,bits,4,order); } + table_cook_put(at+1332,(uint64_t)value->crc_hint,4,order); + table_cook_put(at+1336,value->has_extra ? 1 : 0,1,order); + table_cook_put(at+1340,(uint64_t)value->extra,4,order); + table_cook_put(at+1344,(uint64_t)value->idle_ticks,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t table_mixed_cook_measure(const TableMixed * value) { (void)value; return 1432; } +static SCHEMA_UNUSED int table_mixed_cook(const TableMixed * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<1432) { return 0; } + memset(raw,0,1432); + if(!schema_benchtable_table_mixed_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+1424,UINT64_C(0x439ae459d6f88a8e),8,order); + table_cook_header(raw,SCHEMA_BENCHTABLE_BUILD_VERSION_VALUE,1352,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_benchtable_table_hit_event_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TableHitEvent * value=(const TableHitEvent *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->target_id,4,order); + table_cook_put(at+4,(uint64_t)value->damage,4,order); + table_cook_put(at+8,(uint64_t)value->hit_kind,4,order); + table_cook_put(at+12,value->crit ? 1 : 0,1,order); + return 1; +} +static SCHEMA_UNUSED int schema_benchtable_table_chat_event_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TableChatEvent * value=(const TableChatEvent *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->channel,4,order); + table_cook_put(at+4,(uint64_t)value->speaker,4,order); + return 1; +} +static SCHEMA_UNUSED int schema_benchtable_table_pickup_event_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TablePickupEvent * value=(const TablePickupEvent *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->item_id,4,order); + table_cook_put(at+4,(uint64_t)value->amount,4,order); + return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/generated/bench/tables/c/BenchtableView.c b/generated/bench/tables/c/BenchtableView.c new file mode 100644 index 000000000..0a72ca873 --- /dev/null +++ b/generated/bench/tables/c/BenchtableView.c @@ -0,0 +1,67 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "BenchtableView.h" +const UnitViewInfo * schema_benchtable_unit_view_(void) +{ + static const ViewVariant TableWeapon_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Fists", 0xa790eee12766cf0cull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Pistol", 0x9fbfefa835da8476ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Shotgun", 0x188bbb1783928a95ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Rifle", 0x2c3667b9c2f272f1ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "Sniper", 0x0ca8b41b00d755f6ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 6, "Smg", 0x985fc819fab21a4eull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 7, "Rocket", 0x229d0447c3086a55ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 8, "Grenade", 0x011c7b49a7228f9dull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 9, "Plasma", 0x89f7566e1123e15full, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 10, "Railgun", 0x8d7f25318b3b4469ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 11, "Flamer", 0xd9cd0dcc285b7816ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 12, "Mine", 0x04dc16aea8ff5276ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 13, "Turret", 0x35e53bc341129217ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 14, "Drone", 0x2cc8282fb0de8831ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 15, "Repair", 0x20bada21bc8cb334ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant TableDamage_view_variants[] = { + { 0, "Bleeding", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Burning", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Stunned", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Slowed", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Poisoned", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "Shielded", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 6, "Airborne", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 7, "Downed", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant TableEvent_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "hit", 0x33732819300680aaull, "TableHitEvent", &schema_benchtable_table_hit_event_info_, NULL, TableDocNone, 0, NULL }, + { 2, "chat", 0xf2a38d910b5b348bull, "TableChatEvent", &schema_benchtable_table_chat_event_info_, NULL, TableDocNone, 0, NULL }, + { 3, "pickup", 0x9fa3a41c86ecb765ull, "TablePickupEvent", &schema_benchtable_table_pickup_event_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "TableWeapon", "BenchTable.schema", 15, 8, 16, TableWeapon_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary flags_decls[] = { + { "TableDamage", "BenchTable.schema", 7, 8, 8, TableDamage_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary unions[] = { + { "TableEvent", "BenchTable.schema", 3, 8, 4, TableEvent_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "TableChatEvent", "BenchTable.schema", 0, &schema_benchtable_table_chat_event_info_, TableDocNone, 0, NULL }, + { "TableHitEvent", "BenchTable.schema", 0, &schema_benchtable_table_hit_event_info_, TableDocNone, 0, NULL }, + { "TablePickupEvent", "BenchTable.schema", 0, &schema_benchtable_table_pickup_event_info_, TableDocNone, 0, NULL }, + }; + static const ViewType tables[] = { + { "TableEntity", "BenchTable.schema", 1, &schema_benchtable_table_entity_info_, TableDocNone, 0, NULL }, + { "TableMixed", "BenchTable.schema", 1, &schema_benchtable_table_mixed_info_, TableDocNone, 0, NULL }, + { "TableStat", "BenchTable.schema", 1, &schema_benchtable_table_stat_info_, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "benchtable", 0x0926221bcb6f475full, + 3, types, + 3, tables, + 1, enums, + 1, flags_decls, + 1, unions, + 0, NULL }; + return &info; +} diff --git a/generated/bench/tables/c/BenchtableView.h b/generated/bench/tables/c/BenchtableView.h new file mode 100644 index 000000000..80a3e0da4 --- /dev/null +++ b/generated/bench/tables/c/BenchtableView.h @@ -0,0 +1,103 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_BENCHTABLE_VIEW_H +#define SCHEMA_BENCHTABLE_VIEW_H +#include +#include +#include +#include "BenchTable.h" +#include "BenchTableTable.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + + +const UnitViewInfo * schema_benchtable_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_benchtable_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/generated/c-ludicrous/LudicrousView.c b/generated/c-ludicrous/LudicrousView.c new file mode 100644 index 000000000..9915745a4 --- /dev/null +++ b/generated/c-ludicrous/LudicrousView.c @@ -0,0 +1,137 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "LudicrousView.h" +static const TableWideRange schema_ludicrous_degenerate_probe_locked_fixed_outside_range_ = { { 18446744073709355008ull, 18446744073709551615ull }, { 18446744073709355008ull, 18446744073709551615ull } }; +static const TableWideRange schema_ludicrous_degenerate_probe_locked_wide_outside_range_ = { { 18446731728030650382ull, 18446744073709551615ull }, { 18446731728030650382ull, 18446744073709551615ull } }; +static const TableFieldInfo schema_ludicrous_degenerate_probe_fields_[] = { + { "locked_fixed", NULL, "fixed(16, 16)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( DegenerateProbe, locked_fixed ), (uint32_t) sizeof( ( (DegenerateProbe *) 0 )->locked_fixed ), 0xffffffffu, 0xffffffffu, NULL, 1, -3.0, -3.0, &schema_ludicrous_degenerate_probe_locked_fixed_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "locked_int", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( DegenerateProbe, locked_int ), (uint32_t) sizeof( ( (DegenerateProbe *) 0 )->locked_int ), 0xffffffffu, 0xffffffffu, NULL, 1, 7.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "locked_wide", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( DegenerateProbe, locked_wide ), (uint32_t) sizeof( ( (DegenerateProbe *) 0 )->locked_wide ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.2345678901234e+13, -1.2345678901234e+13, &schema_ludicrous_degenerate_probe_locked_wide_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( DegenerateProbe, tail ), (uint32_t) sizeof( ( (DegenerateProbe *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_degenerate_probe_info_ = { "DegenerateProbe", (uint32_t) sizeof( DegenerateProbe ), 4, schema_ludicrous_degenerate_probe_fields_, schema_ludicrous_degenerate_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_fixed_probe_angle_outside_range_ = { { 18446744073697755136ull, 18446744073709551615ull }, { 11796480ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_probe_position_outside_range_ = { { 18446744071743471616ull, 18446744073709551615ull }, { 1966080000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_probe_reach_outside_range_ = { { 18446744008173551616ull, 18446744073709551615ull }, { 65536000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_probe_ticks_outside_range_ = { { 0ull, 0ull }, { 1000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_probe_samples_outside_range_ = { { 18446744073709027328ull, 18446744073709551615ull }, { 524288ull, 0ull } }; +static const TableFieldInfo schema_ludicrous_fixed_probe_fields_[] = { + { "angle", NULL, "fixed(16, 16)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedProbe, angle ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->angle ), 0xffffffffu, 0xffffffffu, NULL, 1, -180.0, 180.0, &schema_ludicrous_fixed_probe_angle_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "position", NULL, "fixed(48, 16)", 0x0000000000000000ull, 23, 0, 0, 0, 0, (uint32_t) offsetof( FixedProbe, position ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->position ), 0xffffffffu, 0xffffffffu, NULL, 1, -30000.0, 30000.0, &schema_ludicrous_fixed_probe_position_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "reach", NULL, "fixed(112, 16)", 0x0000000000000000ull, 24, 0, 0, 0, 0, (uint32_t) offsetof( FixedProbe, reach ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->reach ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+06, 1e+06, &schema_ludicrous_fixed_probe_reach_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ticks", NULL, "fixed(32, 0)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedProbe, ticks ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->ticks ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1e+06, &schema_ludicrous_fixed_probe_ticks_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "samples", NULL, "fixed(16, 16)", 0x0000000000000000ull, 22, 1, 0, 0, 2, (uint32_t) offsetof( FixedProbe, samples ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->samples[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, -8.0, 8.0, &schema_ludicrous_fixed_probe_samples_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_fixed_probe_info_ = { "FixedProbe", (uint32_t) sizeof( FixedProbe ), 5, schema_ludicrous_fixed_probe_fields_, schema_ludicrous_fixed_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_fixed_quat_x_outside_range_ = { { 18446744072635809792ull, 18446744073709551615ull }, { 1073741824ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_quat_y_outside_range_ = { { 18446744072635809792ull, 18446744073709551615ull }, { 1073741824ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_quat_z_outside_range_ = { { 18446744072635809792ull, 18446744073709551615ull }, { 1073741824ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_quat_w_outside_range_ = { { 18446744072635809792ull, 18446744073709551615ull }, { 1073741824ull, 0ull } }; +static const TableFieldInfo schema_ludicrous_fixed_quat_fields_[] = { + { "x", NULL, "fixed(2, 30)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedQuat, x ), (uint32_t) sizeof( ( (FixedQuat *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, &schema_ludicrous_fixed_quat_x_outside_range_, 30, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "fixed(2, 30)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedQuat, y ), (uint32_t) sizeof( ( (FixedQuat *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, &schema_ludicrous_fixed_quat_y_outside_range_, 30, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "fixed(2, 30)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedQuat, z ), (uint32_t) sizeof( ( (FixedQuat *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, &schema_ludicrous_fixed_quat_z_outside_range_, 30, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "w", NULL, "fixed(2, 30)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedQuat, w ), (uint32_t) sizeof( ( (FixedQuat *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, &schema_ludicrous_fixed_quat_w_outside_range_, 30, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_fixed_quat_info_ = { "FixedQuat", (uint32_t) sizeof( FixedQuat ), 4, schema_ludicrous_fixed_quat_fields_, schema_ludicrous_fixed_quat_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_fixed_vec_x_outside_range_ = { { 18446744067155951616ull, 18446744073709551615ull }, { 6553600000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_vec_y_outside_range_ = { { 18446744067155951616ull, 18446744073709551615ull }, { 6553600000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_vec_z_outside_range_ = { { 18446744067155951616ull, 18446744073709551615ull }, { 6553600000ull, 0ull } }; +static const TableFieldInfo schema_ludicrous_fixed_vec_fields_[] = { + { "x", NULL, "fixed(48, 16)", 0x0000000000000000ull, 23, 0, 0, 0, 0, (uint32_t) offsetof( FixedVec, x ), (uint32_t) sizeof( ( (FixedVec *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -100000.0, 100000.0, &schema_ludicrous_fixed_vec_x_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "fixed(48, 16)", 0x0000000000000000ull, 23, 0, 0, 0, 0, (uint32_t) offsetof( FixedVec, y ), (uint32_t) sizeof( ( (FixedVec *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -100000.0, 100000.0, &schema_ludicrous_fixed_vec_y_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "fixed(48, 16)", 0x0000000000000000ull, 23, 0, 0, 0, 0, (uint32_t) offsetof( FixedVec, z ), (uint32_t) sizeof( ( (FixedVec *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -100000.0, 100000.0, &schema_ludicrous_fixed_vec_z_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_fixed_vec_info_ = { "FixedVec", (uint32_t) sizeof( FixedVec ), 3, schema_ludicrous_fixed_vec_fields_, schema_ludicrous_fixed_vec_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_ludicrous_ludicrous_state_mode_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Cruise", 0x0000000000000000ull }, + { "Warp", 0x0000000000000000ull }, + { "Ludicrous", 0x0000000000000000ull }, +}; +static const TableWideRange schema_ludicrous_ludicrous_state_keys_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableWideRange schema_ludicrous_ludicrous_state_target_id_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableFieldInfo schema_ludicrous_ludicrous_state_fields_[] = { + { "mode", NULL, "DriveMode", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, mode ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->mode ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_ludicrous_ludicrous_state_mode_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "probe", NULL, "FixedProbe", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, probe ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->probe ), 0xffffffffu, 0xffffffffu, &schema_ludicrous_fixed_probe_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "wide", NULL, "WideProbe", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, wide ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->wide ), 0xffffffffu, 0xffffffffu, &schema_ludicrous_wide_probe_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "keys", NULL, "uint128", 0x0000000000000000ull, 19, 1, 1, 0, 4, (uint32_t) offsetof( LudicrousState, keys ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->keys[0] ), (uint32_t) offsetof( LudicrousState, keys_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_ludicrous_ludicrous_state_keys_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_target", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, has_target ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->has_target ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "target_id", NULL, "uint128", 0x0000000000000000ull, 19, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, target_id ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_ludicrous_ludicrous_state_target_id_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "has_target", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_ludicrous_state_info_ = { "LudicrousState", (uint32_t) sizeof( LudicrousState ), 6, schema_ludicrous_ludicrous_state_fields_, schema_ludicrous_ludicrous_state_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_unsigned_probe_angle_outside_range_ = { { 0ull, 0ull }, { 23592960ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_span_outside_range_ = { { 0ull, 0ull }, { 18446744073709486080ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_reach_outside_range_ = { { 0ull, 0ull }, { 131072000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_ticks_outside_range_ = { { 0ull, 0ull }, { 1000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_samples_outside_range_ = { { 0ull, 0ull }, { 1048576ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_locked_outside_range_ = { { 196608ull, 0ull }, { 196608ull, 0ull } }; +static const TableFieldInfo schema_ludicrous_unsigned_probe_fields_[] = { + { "angle", NULL, "ufixed(16, 16)", 0x0000000000000000ull, 27, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, angle ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->angle ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 360.0, &schema_ludicrous_unsigned_probe_angle_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "span", NULL, "ufixed(48, 16)", 0x0000000000000000ull, 28, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, span ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, &schema_ludicrous_unsigned_probe_span_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "reach", NULL, "ufixed(112, 16)", 0x0000000000000000ull, 29, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, reach ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->reach ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2e+06, &schema_ludicrous_unsigned_probe_reach_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ticks", NULL, "ufixed(32, 0)", 0x0000000000000000ull, 27, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, ticks ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->ticks ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1e+06, &schema_ludicrous_unsigned_probe_ticks_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "samples", NULL, "ufixed(16, 16)", 0x0000000000000000ull, 27, 1, 0, 0, 2, (uint32_t) offsetof( UnsignedProbe, samples ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->samples[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 16.0, &schema_ludicrous_unsigned_probe_samples_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "locked", NULL, "ufixed(16, 16)", 0x0000000000000000ull, 27, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, locked ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->locked ), 0xffffffffu, 0xffffffffu, NULL, 1, 3.0, 3.0, &schema_ludicrous_unsigned_probe_locked_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, tail ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_unsigned_probe_info_ = { "UnsignedProbe", (uint32_t) sizeof( UnsignedProbe ), 7, schema_ludicrous_unsigned_probe_fields_, schema_ludicrous_unsigned_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_wide_probe_entity_id_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableWideRange schema_ludicrous_wide_probe_energy_outside_range_ = { { 18446744068709551616ull, 18446744073709551615ull }, { 5000000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_wide_probe_flux_outside_range_ = { { 0ull, 18446744004990074880ull }, { 0ull, 68719476736ull } }; +static const TableWideRange schema_ludicrous_wide_probe_bias_outside_range_ = { { 18446744073709550616ull, 18446744073709551615ull }, { 1000ull, 0ull } }; +static const TableWideRange schema_ludicrous_wide_probe_seed_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableFieldInfo schema_ludicrous_wide_probe_fields_[] = { + { "entity_id", NULL, "uint128", 0x0000000000000000ull, 19, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, entity_id ), (uint32_t) sizeof( ( (WideProbe *) 0 )->entity_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_ludicrous_wide_probe_entity_id_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "energy", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, energy ), (uint32_t) sizeof( ( (WideProbe *) 0 )->energy ), 0xffffffffu, 0xffffffffu, NULL, 1, -5e+09, 5e+09, &schema_ludicrous_wide_probe_energy_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flux", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, flux ), (uint32_t) sizeof( ( (WideProbe *) 0 )->flux ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.2676506002282294e+30, 1.2676506002282294e+30, &schema_ludicrous_wide_probe_flux_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "bias", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, bias ), (uint32_t) sizeof( ( (WideProbe *) 0 )->bias ), 0xffffffffu, 0xffffffffu, NULL, 1, -1000.0, 1000.0, &schema_ludicrous_wide_probe_bias_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "seed", NULL, "uint128", 0x0000000000000000ull, 19, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, seed ), (uint32_t) sizeof( ( (WideProbe *) 0 )->seed ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_ludicrous_wide_probe_seed_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_wide_probe_info_ = { "WideProbe", (uint32_t) sizeof( WideProbe ), 5, schema_ludicrous_wide_probe_fields_, schema_ludicrous_wide_probe_reset_raw_, TableDocNone, 0, NULL }; + +const UnitViewInfo * schema_ludicrous_unit_view_(void) +{ + static const ViewVariant DriveMode_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Cruise", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Warp", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Ludicrous", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "DriveMode", "Ludicrous.schema", 3, 8, 4, DriveMode_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "DegenerateProbe", "Ludicrous.schema", 0, &schema_ludicrous_degenerate_probe_info_, TableDocNone, 0, NULL }, + { "FixedProbe", "Ludicrous.schema", 0, &schema_ludicrous_fixed_probe_info_, TableDocNone, 0, NULL }, + { "FixedQuat", "Ludicrous.schema", 0, &schema_ludicrous_fixed_quat_info_, TableDocNone, 0, NULL }, + { "FixedVec", "Ludicrous.schema", 0, &schema_ludicrous_fixed_vec_info_, TableDocNone, 0, NULL }, + { "LudicrousState", "Ludicrous.schema", 0, &schema_ludicrous_ludicrous_state_info_, TableDocNone, 0, NULL }, + { "UnsignedProbe", "Ludicrous.schema", 0, &schema_ludicrous_unsigned_probe_info_, TableDocNone, 0, NULL }, + { "WideProbe", "Ludicrous.schema", 0, &schema_ludicrous_wide_probe_info_, TableDocNone, 0, NULL }, + }; + static const ViewConstant constants[] = { + { "MaxWorldUnits", "Ludicrous.schema", "int64", 0, INT64_C(30000), 0.0, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "ludicrous", 0xf4a226f4166d919bull, + 7, types, + 0, NULL, + 1, enums, + 0, NULL, + 0, NULL, + 1, constants }; + return &info; +} diff --git a/generated/c-ludicrous/LudicrousView.h b/generated/c-ludicrous/LudicrousView.h new file mode 100644 index 000000000..14c44bc81 --- /dev/null +++ b/generated/c-ludicrous/LudicrousView.h @@ -0,0 +1,431 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_LUDICROUS_VIEW_H +#define SCHEMA_LUDICROUS_VIEW_H +#include +#include +#include +#include "Ludicrous.h" +#ifndef SCHEMA_LUDICROUS_TABLE_PRIMITIVES +#define SCHEMA_LUDICROUS_TABLE_PRIMITIVES + +/* THE CODEC DOES NOT DEPEND ON THE COMPILER'S INLINING BUDGET. A table of a + realistic field count emits one large body per type, and the cursor a body + writes through lives in the caller's TableWriter: across a call boundary that + cursor round-trips through memory, and a uint8_t * store may alias the writer + itself, so every put reloads it. When a budget runs out mid-body the codec + silently degrades to that shape. Forcing the primitives and the fixed-class + bodies inline is what keeps the cursor in registers and lets adjacent + constant framing bytes merge into one store. + + The spelling is FEATURE TESTED rather than assumed, exactly as the packet + emitter's own inlining demand is (SPEC §6.1's C column). + + IT CARRIES THE PACKAGE for the reason every other name here does, and that + reason is the LINKER rather than the preprocessor: two units of the same + schema family link into one program — two generations of one schema is the + case the conformance driver itself is — so nothing this backend emits may + be spelled the same way by two packages. They do NOT share a translation + unit and cannot: C has no namespace, so two units' headers in one TU + redefine TableReport, TableWriter and every other runtime name, which is the + packet emitter's standing limit (SPEC §6.1) and not something tables + changed. A per-package macro is what keeps the LINK legal. */ +#ifndef SCHEMA_LUDICROUS_TABLE_INLINE +#if defined( _MSC_VER ) +#define SCHEMA_LUDICROUS_TABLE_INLINE __forceinline +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_LUDICROUS_TABLE_INLINE inline __attribute__(( always_inline )) +#else +#define SCHEMA_LUDICROUS_TABLE_INLINE inline +#endif +#endif + +/* A COMPILE-TIME assertion under -std=c99, which has none of its own. C11's + _Static_assert carries the message into the build log verbatim and is used + wherever the compiler has it; everywhere else the negative array bound is + the same refusal with a tag a reader can find. The tag is unique per record + and field, and each record is asserted by the ONE header that declares it, + so no two of these ever meet. */ +#ifndef SCHEMA_TABLE_STATIC_ASSERT +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) _Static_assert( cond, message ) +#elif defined( __cplusplus ) && __cplusplus >= 201103L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) static_assert( cond, message ) +#else +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) typedef char schema_table_assert_##tag[ ( cond ) ? 1 : -1 ] +#endif +#endif + +/* A record's ALIGNMENT, which the cook's layout contract asserts and which the + cook's Open compares the header's alignment word against (§7.1, §20.3). + C99 has no alignof either; C11's _Alignof, the two GNU spellings and MSVC's + cover every compiler this repo builds under, and the last form is the + portable definition of the same number. */ +#ifndef SCHEMA_TABLE_ALIGNOF +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_ALIGNOF( T ) _Alignof( T ) +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof__( T ) +#elif defined( _MSC_VER ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof( T ) +#else +#define SCHEMA_TABLE_ALIGNOF( T ) ( (size_t) offsetof( struct { char schema_pad_; T schema_member_; }, schema_member_ ) ) +#endif +#endif + +/* The table-wire read report — the permissive contract's ledger. Silence + (all zero) means the data matched this reader's schema exactly. */ +typedef struct TableReport +{ + int32_t unknown; /* unknown field ids skipped (newer data) */ + int32_t kind_mismatch; /* known id, changed type — skipped, never misdecoded */ + int32_t clamped; /* out-of-range values clamped to declared bounds */ + /* a key the TEXT form saw twice: last wins, and the repeat is counted + (docs/SPEC-TABLES.md §16.2). The wire never raises it — a body carrying an + id twice is legal input whose last occurrence wins, silently (§3). */ + int32_t duplicate; + int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ +} TableReport; + +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + +/* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- + + Static field descriptors for every type in the table closure: name, wire + id/kind, storage offset, bounds, ranges, enum names and branch guards — + enough to walk, print, diff, edit or bind any table value at runtime with + no schema files. _table_type() returns 's descriptor. */ + +struct TableTypeInfo; +struct TableFieldInfo; + +/* One arm of a union field: where its payload sits inside the union's storage + and what its payload looks like. The arm's NAME and its table-wire id ride + in the field's own vocabulary table at the same tag, so nothing is spelled + twice (docs/SPEC-TABLES.md §8). */ +typedef struct TableUnionArmInfo +{ + uint32_t offset; /* offsetof the arm's payload within the union storage */ + const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ +} TableUnionArmInfo; + +/* A union field's shape: the tag, and the arms indexed by it. Arms run + [0, enum_max]; index 0 is the EMPTY arm and carries no payload. */ +typedef struct TableUnionInfo +{ + uint32_t tag_offset; /* offsetof the tag within the union storage */ + uint32_t tag_size; /* sizeof the tag */ + const TableUnionArmInfo * arms; +} TableUnionInfo; + +/* ONE ENTRY OF A VOCABULARY, and the whole of what C++ spells as a pair of + captureless lambdas. An enum's values, a union's arms and a flags field's + BITS are each a named set indexed by [0, enum_max], so the set rides as an + ARRAY indexed the same way: no function per enum, and therefore no name per + enum for a schema to collide with (docs/SPEC-TABLES.md §11). + + name is the variant's spelling, or NULL for a value the declared set does + not name. id is the TABLE-WIRE id it rides under (§5) — the hash of the + variant's name, 0 for the reserved None and for the empty union arm. A + FLAGS field's variants are BIT POSITIONS and have no per-variant wire id + (§4), so has_ids is 0 there and 1 for an enum and a union: a vocabulary + with names and no ids is what says "flags". */ +typedef struct TableVariantInfo +{ + const char * name; + uint64_t id; +} TableVariantInfo; + +/* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// + block carries a doc column pointing at this one object, so absence costs a + unit no string data and a printer concatenates doc columns with no null + test. One definition per translation unit, and the descriptors of a unit + are one, so every absent doc compares equal by address. */ +static SCHEMA_UNUSED const char TableDocNone[1] = ""; + +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + +typedef struct TableFieldInfo +{ + const char * name; /* schema field name, e.g. "health" */ + const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ + const char * type_name; /* schema type name, e.g. "float32", "Grade" */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ + int is_array; /* fixed or counted array (bytes included) */ + int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ + int optional; /* a ?T field: a _present companion decides whether it rides */ + int32_t array_bound; /* array capacity / string max length; 0 for plain scalars */ + uint32_t offset; /* offsetof the storage member */ + uint32_t elem_size; /* sizeof the member (element size for arrays) */ + uint32_t count_offset; /* offsetof the _count/_length companion, or 0xffffffff */ + uint32_t present_offset; /* offsetof the _present companion, or 0xffffffff */ + const struct TableTypeInfo * table; /* nested table's descriptor, or NULL */ + int has_range; /* a declared [min, max] (int or float) */ + double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ + double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; + int64_t enum_max; /* enums: highest valid value (None = 0 always valid); + unions: the arm count (tag range [0, enum_max]); + flags: the highest declared BIT INDEX; else -1 */ + /* the vocabulary, indexed the same way enum_max bounds: an enum's value -> + (name, id), a union's tag -> (arm name, arm id), a FLAGS field's bit + index -> (variant name, 0). NULL for every other kind. */ + const TableVariantInfo * variants; + int has_variant_ids; /* 1 for an enum or a union, 0 for flags (§4) */ + /* an ENUM-KEYED array (docs/SPEC-TABLES.md §2.4): the array has one slot per + variant of key_type_name, the key k at index k-1, and its slots ride + under variant ids rather than positions. keys is the key's vocabulary, + indexed by the KEY — walk [0, array_bound) and ask about index + 1 to + print slots by name. NULL on every other field. */ + const char * key_type_name; + const TableVariantInfo * keys; + int64_t key_max; /* the key enum's highest value, bounding keys */ + /* union fields: the tag and its arms. NULL for every other kind. */ + const TableUnionInfo * arms; + const char * guard; /* branch guard, e.g. "at_rest" or "!at_rest"; "" if unguarded */ + /* what a PERSON wrote about the field (docs/SPEC-TABLES.md §8.1): the /// + block above it, verbatim (SPEC §4.1). It is TableDocNone when there is + none, never NULL. Its tags (SPEC §4.2) follow in declared order, and an + untagged field is 0 beside NULL. Static, constant-initialized, + allocating nothing. */ + const char * doc; + int32_t num_tags; + const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); +} TableFieldInfo; + +typedef struct TableTypeInfo +{ + const char * name; /* schema type name */ + uint32_t size; /* sizeof the storage struct */ + int32_t num_fields; + const TableFieldInfo * fields; + /* put one instance back at its declared defaults, in place. A generic + walker that fills a value has to be able to establish the defaults an + absent field takes, and it holds no type to spell — this is the one + thing the descriptors could not express without it. It is the void * + form of _reset and the same code. */ + void (*reset)( void * storage ); + /* the declaration's own doc and tags, on the same terms as a field's + (docs/SPEC-TABLES.md §8.1) */ + const char * doc; + int32_t num_tags; + const char * const * tags; +} TableTypeInfo; + + + +static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } +static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } +static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } +static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } + +#endif /* SCHEMA_LUDICROUS_TABLE_PRIMITIVES */ + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void degenerate_probe_reset( DegenerateProbe * value ); +static SCHEMA_UNUSED void schema_ludicrous_degenerate_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void fixed_probe_reset( FixedProbe * value ); +static SCHEMA_UNUSED void schema_ludicrous_fixed_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void fixed_quat_reset( FixedQuat * value ); +static SCHEMA_UNUSED void schema_ludicrous_fixed_quat_reset_raw_( void * storage ); +static SCHEMA_UNUSED void fixed_vec_reset( FixedVec * value ); +static SCHEMA_UNUSED void schema_ludicrous_fixed_vec_reset_raw_( void * storage ); +static SCHEMA_UNUSED void ludicrous_state_reset( LudicrousState * value ); +static SCHEMA_UNUSED void schema_ludicrous_ludicrous_state_reset_raw_( void * storage ); +static SCHEMA_UNUSED void unsigned_probe_reset( UnsignedProbe * value ); +static SCHEMA_UNUSED void schema_ludicrous_unsigned_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void wide_probe_reset( WideProbe * value ); +static SCHEMA_UNUSED void schema_ludicrous_wide_probe_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void degenerate_probe_reset( DegenerateProbe * value ) +{ + value->locked_fixed = -196608; + value->locked_int = 7; + value->locked_wide = serialize_int128_make( 18446744073709551615ull, 18446731728030650382ull ); + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_ludicrous_degenerate_probe_reset_raw_( void * storage ) { degenerate_probe_reset( (DegenerateProbe *) storage ); } + +extern const TableTypeInfo schema_ludicrous_degenerate_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * degenerate_probe_table_type(void) { return &schema_ludicrous_degenerate_probe_info_; } +static SCHEMA_UNUSED void fixed_probe_reset( FixedProbe * value ) +{ + value->angle = 0; + value->position = 0; + value->reach = serialize_int128_make( 0ull, 0ull ); + value->ticks = 0; + memset( value->samples, 0, sizeof( value->samples ) ); +} + +static SCHEMA_UNUSED void schema_ludicrous_fixed_probe_reset_raw_( void * storage ) { fixed_probe_reset( (FixedProbe *) storage ); } + +extern const TableTypeInfo schema_ludicrous_fixed_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * fixed_probe_table_type(void) { return &schema_ludicrous_fixed_probe_info_; } +static SCHEMA_UNUSED void fixed_quat_reset( FixedQuat * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; + value->w = 1073741824; +} + +static SCHEMA_UNUSED void schema_ludicrous_fixed_quat_reset_raw_( void * storage ) { fixed_quat_reset( (FixedQuat *) storage ); } + +extern const TableTypeInfo schema_ludicrous_fixed_quat_info_; +static SCHEMA_UNUSED const TableTypeInfo * fixed_quat_table_type(void) { return &schema_ludicrous_fixed_quat_info_; } +static SCHEMA_UNUSED void fixed_vec_reset( FixedVec * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; +} + +static SCHEMA_UNUSED void schema_ludicrous_fixed_vec_reset_raw_( void * storage ) { fixed_vec_reset( (FixedVec *) storage ); } + +extern const TableTypeInfo schema_ludicrous_fixed_vec_info_; +static SCHEMA_UNUSED const TableTypeInfo * fixed_vec_table_type(void) { return &schema_ludicrous_fixed_vec_info_; } +static SCHEMA_UNUSED void ludicrous_state_reset( LudicrousState * value ) +{ + value->mode = DRIVE_MODE_NONE; + fixed_probe_reset( &value->probe ); + wide_probe_reset( &value->wide ); + memset( value->keys, 0, sizeof( value->keys ) ); + value->keys_count = 0; + value->has_target = 0; + value->target_id = serialize_uint128_make( 0ull, 0ull ); +} + +static SCHEMA_UNUSED void schema_ludicrous_ludicrous_state_reset_raw_( void * storage ) { ludicrous_state_reset( (LudicrousState *) storage ); } + +extern const TableTypeInfo schema_ludicrous_ludicrous_state_info_; +static SCHEMA_UNUSED const TableTypeInfo * ludicrous_state_table_type(void) { return &schema_ludicrous_ludicrous_state_info_; } +static SCHEMA_UNUSED void unsigned_probe_reset( UnsignedProbe * value ) +{ + value->angle = 0; + value->span = 0; + value->reach = serialize_uint128_make( 0ull, 0ull ); + value->ticks = 0; + memset( value->samples, 0, sizeof( value->samples ) ); + value->locked = 196608; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_ludicrous_unsigned_probe_reset_raw_( void * storage ) { unsigned_probe_reset( (UnsignedProbe *) storage ); } + +extern const TableTypeInfo schema_ludicrous_unsigned_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * unsigned_probe_table_type(void) { return &schema_ludicrous_unsigned_probe_info_; } +static SCHEMA_UNUSED void wide_probe_reset( WideProbe * value ) +{ + value->entity_id = serialize_uint128_make( 0ull, 0ull ); + value->energy = serialize_int128_make( 0ull, 0ull ); + value->flux = serialize_int128_make( 0ull, 0ull ); + value->bias = serialize_int128_make( 18446744073709551615ull, 18446744073709551366ull ); + value->seed = serialize_uint128_make( 2ull, 0ull ); +} + +static SCHEMA_UNUSED void schema_ludicrous_wide_probe_reset_raw_( void * storage ) { wide_probe_reset( (WideProbe *) storage ); } + +extern const TableTypeInfo schema_ludicrous_wide_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * wide_probe_table_type(void) { return &schema_ludicrous_wide_probe_info_; } +const UnitViewInfo * schema_ludicrous_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_ludicrous_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/generated/c/ExampleView.c b/generated/c/ExampleView.c new file mode 100644 index 000000000..1a4b6f3f9 --- /dev/null +++ b/generated/c/ExampleView.c @@ -0,0 +1,934 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "ExampleView.h" +static const TableFieldInfo schema_example_arm_align_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmAlign, lead ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmAlign, flag ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "s", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 4, (uint32_t) offsetof( ArmAlign, s ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->s ), (uint32_t) offsetof( ArmAlign, s_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(10)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmAlign, b ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1023.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmAlign, tail ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arm_align_info_ = { "ArmAlign", (uint32_t) sizeof( ArmAlign ), 5, schema_example_arm_align_fields_, schema_example_arm_align_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arm_array_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmArray, lead ), (uint32_t) sizeof( ( (ArmArray *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmArray, flag ), (uint32_t) sizeof( ( (ArmArray *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "uint16", 0x0000000000000000ull, 7, 1, 1, 0, 3, (uint32_t) offsetof( ArmArray, items ), (uint32_t) sizeof( ( (ArmArray *) 0 )->items[0] ), (uint32_t) offsetof( ArmArray, items_count ), 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmArray, b ), (uint32_t) sizeof( ( (ArmArray *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmArray, tail ), (uint32_t) sizeof( ( (ArmArray *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arm_array_info_ = { "ArmArray", (uint32_t) sizeof( ArmArray ), 5, schema_example_arm_array_fields_, schema_example_arm_array_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arm_empty_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmEmpty, lead ), (uint32_t) sizeof( ( (ArmEmpty *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmEmpty, flag ), (uint32_t) sizeof( ( (ArmEmpty *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "a", NULL, "bits(19)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ArmEmpty, a ), (uint32_t) sizeof( ( (ArmEmpty *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 524287.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmEmpty, tail ), (uint32_t) sizeof( ( (ArmEmpty *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arm_empty_info_ = { "ArmEmpty", (uint32_t) sizeof( ArmEmpty ), 4, schema_example_arm_empty_fields_, schema_example_arm_empty_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arms_agree_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, lead ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, flag ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "a", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, a ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, b ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, tail ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arms_agree_info_ = { "ArmsAgree", (uint32_t) sizeof( ArmsAgree ), 5, schema_example_arms_agree_fields_, schema_example_arms_agree_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arms_disagree_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, lead ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, flag ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "a", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, a ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, b ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, tail ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arms_disagree_info_ = { "ArmsDisagree", (uint32_t) sizeof( ArmsDisagree ), 5, schema_example_arms_disagree_fields_, schema_example_arms_disagree_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arms_nested_fields_[] = { + { "lead", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, lead ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "outer", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, outer ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->outer ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "inner", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, inner ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->inner ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "outer", TableDocNone, 0, NULL, 0, NULL }, + { "x", NULL, "bits(29)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, x ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 5.36870911e+08, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "outer && inner", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, y ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "outer && !inner", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "bits(13)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, z ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!outer", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(6)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, tail ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 63.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arms_nested_info_ = { "ArmsNested", (uint32_t) sizeof( ArmsNested ), 7, schema_example_arms_nested_fields_, schema_example_arms_nested_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arr_eleven_fields_[] = { + { "items", NULL, "Eleven", 0x0000000000000000ull, 13, 1, 0, 0, 9, (uint32_t) offsetof( ArrEleven, items ), (uint32_t) sizeof( ( (ArrEleven *) 0 )->items[0] ), 0xffffffffu, 0xffffffffu, &schema_example_eleven_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arr_eleven_info_ = { "ArrEleven", (uint32_t) sizeof( ArrEleven ), 1, schema_example_arr_eleven_fields_, schema_example_arr_eleven_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arr_nested_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArrNested, lead ), (uint32_t) sizeof( ( (ArrNested *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "Eleven", 0x0000000000000000ull, 13, 1, 1, 0, 4, (uint32_t) offsetof( ArrNested, items ), (uint32_t) sizeof( ( (ArrNested *) 0 )->items[0] ), (uint32_t) offsetof( ArrNested, items_count ), 0xffffffffu, &schema_example_eleven_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArrNested, tail ), (uint32_t) sizeof( ( (ArrNested *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arr_nested_info_ = { "ArrNested", (uint32_t) sizeof( ArrNested ), 3, schema_example_arr_nested_fields_, schema_example_arr_nested_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arr_tri3_fields_[] = { + { "items", NULL, "Tri3", 0x0000000000000000ull, 13, 1, 1, 0, 10, (uint32_t) offsetof( ArrTri3, items ), (uint32_t) sizeof( ( (ArrTri3 *) 0 )->items[0] ), (uint32_t) offsetof( ArrTri3, items_count ), 0xffffffffu, &schema_example_tri3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arr_tri3_info_ = { "ArrTri3", (uint32_t) sizeof( ArrTri3 ), 1, schema_example_arr_tri3_fields_, schema_example_arr_tri3_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_arr_uneven_items_outside_variants_[] = { + { "None", 0 }, + { "narrow", 0x0000000000000000ull }, + { "wide", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_arr_uneven_items_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( Uneven, as.narrow ), &schema_example_narrow_info_, NULL, (uint32_t)sizeof( ((Uneven *)0)->as.narrow ) }, + { (uint32_t)offsetof( Uneven, as.wide ), &schema_example_wide_info_, NULL, (uint32_t)sizeof( ((Uneven *)0)->as.wide ) }, +}; +static const TableUnionInfo schema_example_arr_uneven_items_outside_union_ = { (uint32_t) offsetof( Uneven, type ), (uint32_t) sizeof( ( (Uneven *) 0 )->type ), schema_example_arr_uneven_items_outside_arms_ }; +static const TableFieldInfo schema_example_arr_uneven_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArrUneven, lead ), (uint32_t) sizeof( ( (ArrUneven *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "Uneven", 0x0000000000000000ull, 15, 1, 1, 0, 3, (uint32_t) offsetof( ArrUneven, items ), (uint32_t) sizeof( ( (ArrUneven *) 0 )->items[0] ), (uint32_t) offsetof( ArrUneven, items_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_arr_uneven_items_outside_variants_, 1, NULL, NULL, -1, &schema_example_arr_uneven_items_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArrUneven, tail ), (uint32_t) sizeof( ( (ArrUneven *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arr_uneven_info_ = { "ArrUneven", (uint32_t) sizeof( ArrUneven ), 3, schema_example_arr_uneven_fields_, schema_example_arr_uneven_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_block_fields_[] = { + { "data", NULL, "bytes", 0x0000000000000000ull, 6, 1, 1, 0, 2000, (uint32_t) offsetof( Block, data ), (uint32_t) sizeof( ( (Block *) 0 )->data[0] ), (uint32_t) offsetof( Block, data_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_block_info_ = { "Block", (uint32_t) sizeof( Block ), 1, schema_example_block_fields_, schema_example_block_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_chat_fields_[] = { + { "text", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 256, (uint32_t) offsetof( Chat, text ), (uint32_t) sizeof( ( (Chat *) 0 )->text ), (uint32_t) offsetof( Chat, text_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_chat_info_ = { "Chat", (uint32_t) sizeof( Chat ), 1, schema_example_chat_fields_, schema_example_chat_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_compressed_probe_fields_[] = { + { "boundary", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( CompressedProbe, boundary ), (uint32_t) sizeof( ( (CompressedProbe *) 0 )->boundary ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "offset", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( CompressedProbe, offset ), (uint32_t) sizeof( ( (CompressedProbe *) 0 )->offset ), 0xffffffffu, 0xffffffffu, NULL, 1, -5.0, 5.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_compressed_probe_info_ = { "CompressedProbe", (uint32_t) sizeof( CompressedProbe ), 2, schema_example_compressed_probe_fields_, schema_example_compressed_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_default_arm_fields_[] = { + { "entries", NULL, "ProbeConfig", 0x0000000000000000ull, 13, 1, 1, 0, 2, (uint32_t) offsetof( DefaultArm, entries ), (uint32_t) sizeof( ( (DefaultArm *) 0 )->entries[0] ), (uint32_t) offsetof( DefaultArm, entries_count ), 0xffffffffu, &schema_example_probe_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "marker", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( DefaultArm, marker ), (uint32_t) sizeof( ( (DefaultArm *) 0 )->marker ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_default_arm_info_ = { "DefaultArm", (uint32_t) sizeof( DefaultArm ), 2, schema_example_default_arm_fields_, schema_example_default_arm_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_default_bulk_arm_fields_[] = { + { "payload", NULL, "DefaultArm", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( DefaultBulkArm, payload ), (uint32_t) sizeof( ( (DefaultBulkArm *) 0 )->payload ), 0xffffffffu, 0xffffffffu, &schema_example_default_arm_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "data", NULL, "bytes", 0x0000000000000000ull, 6, 1, 1, 0, 2, (uint32_t) offsetof( DefaultBulkArm, data ), (uint32_t) sizeof( ( (DefaultBulkArm *) 0 )->data[0] ), (uint32_t) offsetof( DefaultBulkArm, data_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_default_bulk_arm_info_ = { "DefaultBulkArm", (uint32_t) sizeof( DefaultBulkArm ), 2, schema_example_default_bulk_arm_fields_, schema_example_default_bulk_arm_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_default_choice_packet_choice_outside_variants_[] = { + { "None", 0 }, + { "first", 0x0000000000000000ull }, + { "second", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_default_choice_packet_choice_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( DefaultChoice, as.first ), &schema_example_default_arm_info_, NULL, (uint32_t)sizeof( ((DefaultChoice *)0)->as.first ) }, + { (uint32_t)offsetof( DefaultChoice, as.second ), &schema_example_default_arm_info_, NULL, (uint32_t)sizeof( ((DefaultChoice *)0)->as.second ) }, +}; +static const TableUnionInfo schema_example_default_choice_packet_choice_outside_union_ = { (uint32_t) offsetof( DefaultChoice, type ), (uint32_t) sizeof( ( (DefaultChoice *) 0 )->type ), schema_example_default_choice_packet_choice_outside_arms_ }; +static const TableFieldInfo schema_example_default_choice_packet_fields_[] = { + { "choice", NULL, "DefaultChoice", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( DefaultChoicePacket, choice ), (uint32_t) sizeof( ( (DefaultChoicePacket *) 0 )->choice ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_default_choice_packet_choice_outside_variants_, 1, NULL, NULL, -1, &schema_example_default_choice_packet_choice_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_default_choice_packet_info_ = { "DefaultChoicePacket", (uint32_t) sizeof( DefaultChoicePacket ), 1, schema_example_default_choice_packet_fields_, schema_example_default_choice_packet_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_eleven_fields_[] = { + { "a", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Eleven, a ), (uint32_t) sizeof( ( (Eleven *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Eleven, b ), (uint32_t) sizeof( ( (Eleven *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_eleven_info_ = { "Eleven", (uint32_t) sizeof( Eleven ), 2, schema_example_eleven_fields_, schema_example_eleven_reset_raw_, TableDocNone, 0, NULL }; + +const TableTypeInfo schema_example_empty_a_info_ = { "EmptyA", (uint32_t) sizeof( EmptyA ), 0, NULL, schema_example_empty_a_reset_raw_, TableDocNone, 0, NULL }; + +const TableTypeInfo schema_example_empty_b_info_ = { "EmptyB", (uint32_t) sizeof( EmptyB ), 0, NULL, schema_example_empty_b_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_expression_probe_fields_[] = { + { "hardpoint_index", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ExpressionProbe, hardpoint_index ), (uint32_t) sizeof( ( (ExpressionProbe *) 0 )->hardpoint_index ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "spin_rate", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ExpressionProbe, spin_rate ), (uint32_t) sizeof( ( (ExpressionProbe *) 0 )->spin_rate ), 0xffffffffu, 0xffffffffu, NULL, 1, 1024.0, 2048.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_expression_probe_info_ = { "ExpressionProbe", (uint32_t) sizeof( ExpressionProbe ), 2, schema_example_expression_probe_fields_, schema_example_expression_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_extreme_probe_fields_[] = { + { "floor_bound", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, floor_bound ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->floor_bound ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "doubled_floor", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, doubled_floor ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->doubled_floor ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ceiling_range", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, ceiling_range ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->ceiling_range ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "floor_default", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, floor_default ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->floor_default ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ceiling_default", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, ceiling_default ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->ceiling_default ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_extreme_probe_info_ = { "ExtremeProbe", (uint32_t) sizeof( ExtremeProbe ), 5, schema_example_extreme_probe_fields_, schema_example_extreme_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_extreme_row_fields_[] = { + { "clamped_floor", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeRow, clamped_floor ), (uint32_t) sizeof( ( (ExtremeRow *) 0 )->clamped_floor ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "clamped_ceiling", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeRow, clamped_ceiling ), (uint32_t) sizeof( ( (ExtremeRow *) 0 )->clamped_ceiling ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "floor_def", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeRow, floor_def ), (uint32_t) sizeof( ( (ExtremeRow *) 0 )->floor_def ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ceiling_def", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeRow, ceiling_def ), (uint32_t) sizeof( ( (ExtremeRow *) 0 )->ceiling_def ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_extreme_row_info_ = { "ExtremeRow", (uint32_t) sizeof( ExtremeRow ), 4, schema_example_extreme_row_fields_, schema_example_extreme_row_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_f13_fields_[] = { + { "items", NULL, "uint16", 0x0000000000000000ull, 7, 1, 0, 0, 7, (uint32_t) offsetof( F13, items ), (uint32_t) sizeof( ( (F13 *) 0 )->items[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_f13_info_ = { "F13", (uint32_t) sizeof( F13 ), 1, schema_example_f13_fields_, schema_example_f13_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_handle_fields_[] = { + { "object_id", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( Handle, object_id ), (uint32_t) sizeof( ( (Handle *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 9999.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_sequence", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Handle, object_sequence ), (uint32_t) sizeof( ( (Handle *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_handle_info_ = { "Handle", (uint32_t) sizeof( Handle ), 2, schema_example_handle_fields_, schema_example_handle_reset_raw_, TableDocNone, 0, NULL }; + +const TableTypeInfo schema_example_heartbeat_info_ = { "Heartbeat", (uint32_t) sizeof( Heartbeat ), 0, NULL, schema_example_heartbeat_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_holds_empty_union_u_outside_variants_[] = { + { "None", 0 }, + { "a", 0x0000000000000000ull }, + { "b", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_holds_empty_union_u_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( EmptyUnion, as.a ), &schema_example_empty_a_info_, NULL, (uint32_t)sizeof( ((EmptyUnion *)0)->as.a ) }, + { (uint32_t)offsetof( EmptyUnion, as.b ), &schema_example_empty_b_info_, NULL, (uint32_t)sizeof( ((EmptyUnion *)0)->as.b ) }, +}; +static const TableUnionInfo schema_example_holds_empty_union_u_outside_union_ = { (uint32_t) offsetof( EmptyUnion, type ), (uint32_t) sizeof( ( (EmptyUnion *) 0 )->type ), schema_example_holds_empty_union_u_outside_arms_ }; +static const TableFieldInfo schema_example_holds_empty_union_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( HoldsEmptyUnion, lead ), (uint32_t) sizeof( ( (HoldsEmptyUnion *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u", NULL, "EmptyUnion", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( HoldsEmptyUnion, u ), (uint32_t) sizeof( ( (HoldsEmptyUnion *) 0 )->u ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_holds_empty_union_u_outside_variants_, 1, NULL, NULL, -1, &schema_example_holds_empty_union_u_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( HoldsEmptyUnion, tail ), (uint32_t) sizeof( ( (HoldsEmptyUnion *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_holds_empty_union_info_ = { "HoldsEmptyUnion", (uint32_t) sizeof( HoldsEmptyUnion ), 3, schema_example_holds_empty_union_fields_, schema_example_holds_empty_union_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_holds_uneven_u_outside_variants_[] = { + { "None", 0 }, + { "narrow", 0x0000000000000000ull }, + { "wide", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_holds_uneven_u_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( Uneven, as.narrow ), &schema_example_narrow_info_, NULL, (uint32_t)sizeof( ((Uneven *)0)->as.narrow ) }, + { (uint32_t)offsetof( Uneven, as.wide ), &schema_example_wide_info_, NULL, (uint32_t)sizeof( ((Uneven *)0)->as.wide ) }, +}; +static const TableUnionInfo schema_example_holds_uneven_u_outside_union_ = { (uint32_t) offsetof( Uneven, type ), (uint32_t) sizeof( ( (Uneven *) 0 )->type ), schema_example_holds_uneven_u_outside_arms_ }; +static const TableFieldInfo schema_example_holds_uneven_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( HoldsUneven, lead ), (uint32_t) sizeof( ( (HoldsUneven *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u", NULL, "Uneven", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( HoldsUneven, u ), (uint32_t) sizeof( ( (HoldsUneven *) 0 )->u ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_holds_uneven_u_outside_variants_, 1, NULL, NULL, -1, &schema_example_holds_uneven_u_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( HoldsUneven, tail ), (uint32_t) sizeof( ( (HoldsUneven *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_holds_uneven_info_ = { "HoldsUneven", (uint32_t) sizeof( HoldsUneven ), 3, schema_example_holds_uneven_fields_, schema_example_holds_uneven_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_input_fields_[] = { + { "stick_x", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, stick_x ), (uint32_t) sizeof( ( (Input *) 0 )->stick_x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "stick_y", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, stick_y ), (uint32_t) sizeof( ( (Input *) 0 )->stick_y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "throttle", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, throttle ), (uint32_t) sizeof( ( (Input *) 0 )->throttle ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "yaw", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, yaw ), (uint32_t) sizeof( ( (Input *) 0 )->yaw ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pitch", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, pitch ), (uint32_t) sizeof( ( (Input *) 0 )->pitch ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "fire", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, fire ), (uint32_t) sizeof( ( (Input *) 0 )->fire ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "alt_fire", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, alt_fire ), (uint32_t) sizeof( ( (Input *) 0 )->alt_fire ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "boost", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, boost ), (uint32_t) sizeof( ( (Input *) 0 )->boost ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "brake", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, brake ), (uint32_t) sizeof( ( (Input *) 0 )->brake ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "aim", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, aim ), (uint32_t) sizeof( ( (Input *) 0 )->aim ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "lock_on", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, lock_on ), (uint32_t) sizeof( ( (Input *) 0 )->lock_on ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "zoom", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, zoom ), (uint32_t) sizeof( ( (Input *) 0 )->zoom ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ping", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, ping ), (uint32_t) sizeof( ( (Input *) 0 )->ping ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_input_info_ = { "Input", (uint32_t) sizeof( Input ), 13, schema_example_input_fields_, schema_example_input_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_input_packet_fields_[] = { + { "synchronize_sequence", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( InputPacket, synchronize_sequence ), (uint32_t) sizeof( ( (InputPacket *) 0 )->synchronize_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "current_frame", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( InputPacket, current_frame ), (uint32_t) sizeof( ( (InputPacket *) 0 )->current_frame ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "start_frame", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( InputPacket, start_frame ), (uint32_t) sizeof( ( (InputPacket *) 0 )->start_frame ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "inputs", NULL, "Input", 0x0000000000000000ull, 13, 1, 1, 0, 16, (uint32_t) offsetof( InputPacket, inputs ), (uint32_t) sizeof( ( (InputPacket *) 0 )->inputs[0] ), (uint32_t) offsetof( InputPacket, inputs_count ), 0xffffffffu, &schema_example_input_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_input_packet_info_ = { "InputPacket", (uint32_t) sizeof( InputPacket ), 4, schema_example_input_packet_fields_, schema_example_input_packet_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_narrow_fields_[] = { + { "n", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Narrow, n ), (uint32_t) sizeof( ( (Narrow *) 0 )->n ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_narrow_info_ = { "Narrow", (uint32_t) sizeof( Narrow ), 1, schema_example_narrow_fields_, schema_example_narrow_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_array_fields_[] = { + { "samples", NULL, "ProbeSample", 0x0000000000000000ull, 13, 1, 0, 0, 2, (uint32_t) offsetof( ProbeArray, samples ), (uint32_t) sizeof( ( (ProbeArray *) 0 )->samples[0] ), 0xffffffffu, 0xffffffffu, &schema_example_probe_sample_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "config", NULL, "ProbeConfig", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ProbeArray, config ), (uint32_t) sizeof( ( (ProbeArray *) 0 )->config ), 0xffffffffu, 0xffffffffu, &schema_example_probe_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_array_info_ = { "ProbeArray", (uint32_t) sizeof( ProbeArray ), 2, schema_example_probe_array_fields_, schema_example_probe_array_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_bits_fields_[] = { + { "small", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, small ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->small ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "boundary", NULL, "bits(33)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, boundary ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->boundary ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8.589934591e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "wide", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, wide ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->wide ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "sensor", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, sensor ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->sensor ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "nonce", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, nonce ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->nonce ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_bits_info_ = { "ProbeBits", (uint32_t) sizeof( ProbeBits ), 5, schema_example_probe_bits_fields_, schema_example_probe_bits_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_probe_collider_shape_outside_variants_[] = { + { "None", 0 }, + { "ring", 0x0000000000000000ull }, + { "slab", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_probe_collider_shape_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( ProbeShape, as.ring ), &schema_example_probe_ring_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.ring ) }, + { (uint32_t)offsetof( ProbeShape, as.slab ), &schema_example_probe_slab_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.slab ) }, +}; +static const TableUnionInfo schema_example_probe_collider_shape_outside_union_ = { (uint32_t) offsetof( ProbeShape, type ), (uint32_t) sizeof( ( (ProbeShape *) 0 )->type ), schema_example_probe_collider_shape_outside_arms_ }; +static const TableVariantInfo schema_example_probe_collider_backup_outside_variants_[] = { + { "None", 0 }, + { "ring", 0x0000000000000000ull }, + { "slab", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_probe_collider_backup_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( ProbeShape, as.ring ), &schema_example_probe_ring_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.ring ) }, + { (uint32_t)offsetof( ProbeShape, as.slab ), &schema_example_probe_slab_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.slab ) }, +}; +static const TableUnionInfo schema_example_probe_collider_backup_outside_union_ = { (uint32_t) offsetof( ProbeShape, type ), (uint32_t) sizeof( ( (ProbeShape *) 0 )->type ), schema_example_probe_collider_backup_outside_arms_ }; +static const TableVariantInfo schema_example_probe_collider_extras_outside_variants_[] = { + { "None", 0 }, + { "ring", 0x0000000000000000ull }, + { "slab", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_probe_collider_extras_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( ProbeShape, as.ring ), &schema_example_probe_ring_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.ring ) }, + { (uint32_t)offsetof( ProbeShape, as.slab ), &schema_example_probe_slab_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.slab ) }, +}; +static const TableUnionInfo schema_example_probe_collider_extras_outside_union_ = { (uint32_t) offsetof( ProbeShape, type ), (uint32_t) sizeof( ( (ProbeShape *) 0 )->type ), schema_example_probe_collider_extras_outside_arms_ }; +static const TableFieldInfo schema_example_probe_collider_fields_[] = { + { "armor", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProbeCollider, armor ), (uint32_t) sizeof( ( (ProbeCollider *) 0 )->armor ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "shape", NULL, "ProbeShape", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( ProbeCollider, shape ), (uint32_t) sizeof( ( (ProbeCollider *) 0 )->shape ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_probe_collider_shape_outside_variants_, 1, NULL, NULL, -1, &schema_example_probe_collider_shape_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "backup", NULL, "ProbeShape", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( ProbeCollider, backup ), (uint32_t) sizeof( ( (ProbeCollider *) 0 )->backup ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_probe_collider_backup_outside_variants_, 1, NULL, NULL, -1, &schema_example_probe_collider_backup_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "extras", NULL, "ProbeShape", 0x0000000000000000ull, 15, 1, 1, 0, 2, (uint32_t) offsetof( ProbeCollider, extras ), (uint32_t) sizeof( ( (ProbeCollider *) 0 )->extras[0] ), (uint32_t) offsetof( ProbeCollider, extras_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_probe_collider_extras_outside_variants_, 1, NULL, NULL, -1, &schema_example_probe_collider_extras_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_collider_info_ = { "ProbeCollider", (uint32_t) sizeof( ProbeCollider ), 4, schema_example_probe_collider_fields_, schema_example_probe_collider_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_probe_config_preferred_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Laser", 0x0000000000000000ull }, + { "Missile", 0x0000000000000000ull }, + { "Railgun", 0x0000000000000000ull }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, +}; +static const TableFieldInfo schema_example_probe_config_fields_[] = { + { "retries", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ProbeConfig, retries ), (uint32_t) sizeof( ( (ProbeConfig *) 0 )->retries ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "preferred", NULL, "Weapon", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ProbeConfig, preferred ), (uint32_t) sizeof( ( (ProbeConfig *) 0 )->preferred ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 15, schema_example_probe_config_preferred_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_config_info_ = { "ProbeConfig", (uint32_t) sizeof( ProbeConfig ), 2, schema_example_probe_config_fields_, schema_example_probe_config_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_header_fields_[] = { + { "version", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProbeHeader, version ), (uint32_t) sizeof( ( (ProbeHeader *) 0 )->version ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "probe_id", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeHeader, probe_id ), (uint32_t) sizeof( ( (ProbeHeader *) 0 )->probe_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_header_info_ = { "ProbeHeader", (uint32_t) sizeof( ProbeHeader ), 2, schema_example_probe_header_fields_, schema_example_probe_header_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_probe_report_flags_outside_variants_[] = { + { "Armed", 0 }, + { "Cloaked", 0 }, + { "Damaged", 0 }, +}; +static const TableFieldInfo schema_example_probe_report_fields_[] = { + { "header", NULL, "ProbeHeader", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ProbeReport, header ), (uint32_t) sizeof( ( (ProbeReport *) 0 )->header ), 0xffffffffu, 0xffffffffu, &schema_example_probe_header_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", NULL, "ProbeFlags", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeReport, flags ), (uint32_t) sizeof( ( (ProbeReport *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_probe_report_flags_outside_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "echo", NULL, "Test", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ProbeReport, echo ), (uint32_t) sizeof( ( (ProbeReport *) 0 )->echo ), 0xffffffffu, 0xffffffffu, &schema_example_test_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_report_info_ = { "ProbeReport", (uint32_t) sizeof( ProbeReport ), 3, schema_example_probe_report_fields_, schema_example_probe_report_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_ring_fields_[] = { + { "radius", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ProbeRing, radius ), (uint32_t) sizeof( ( (ProbeRing *) 0 )->radius ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_ring_info_ = { "ProbeRing", (uint32_t) sizeof( ProbeRing ), 1, schema_example_probe_ring_fields_, schema_example_probe_ring_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_probe_sample_weapon_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Laser", 0x0000000000000000ull }, + { "Missile", 0x0000000000000000ull }, + { "Railgun", 0x0000000000000000ull }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, +}; +static const TableFieldInfo schema_example_probe_sample_fields_[] = { + { "active", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, active ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->active ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "orientation", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, orientation ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->orientation ), 0xffffffffu, 0xffffffffu, NULL, 1, -180.0, 180.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "raw_delta", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, raw_delta ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->raw_delta ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "big_delta", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, big_delta ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->big_delta ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "weapon", NULL, "Weapon", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, weapon ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->weapon ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 15, schema_example_probe_sample_weapon_outside_variants_, 1, NULL, NULL, -1, NULL, "active", TableDocNone, 0, NULL, 0, NULL }, + { "has_target", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, has_target ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->has_target ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "active", TableDocNone, 0, NULL, 0, NULL }, + { "target_id", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, target_id ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "active && has_target", TableDocNone, 0, NULL, 0, NULL }, + { "idle_ticks", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, idle_ticks ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->idle_ticks ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!active", TableDocNone, 0, NULL, 0, NULL }, + { "samples", NULL, "uint16", 0x0000000000000000ull, 7, 1, 1, 0, 8, (uint32_t) offsetof( ProbeSample, samples ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->samples[0] ), (uint32_t) offsetof( ProbeSample, samples_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_sample_info_ = { "ProbeSample", (uint32_t) sizeof( ProbeSample ), 9, schema_example_probe_sample_fields_, schema_example_probe_sample_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_slab_fields_[] = { + { "width", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSlab, width ), (uint32_t) sizeof( ( (ProbeSlab *) 0 )->width ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "height", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSlab, height ), (uint32_t) sizeof( ( (ProbeSlab *) 0 )->height ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_slab_info_ = { "ProbeSlab", (uint32_t) sizeof( ProbeSlab ), 2, schema_example_probe_slab_fields_, schema_example_probe_slab_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_quantized_position_fields_[] = { + { "x", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedPosition, x ), (uint32_t) sizeof( ( (QuantizedPosition *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -8.388608e+06, 8.388608e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedPosition, y ), (uint32_t) sizeof( ( (QuantizedPosition *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -8.388608e+06, 8.388608e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedPosition, z ), (uint32_t) sizeof( ( (QuantizedPosition *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -8.388608e+06, 8.388608e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_quantized_position_info_ = { "QuantizedPosition", (uint32_t) sizeof( QuantizedPosition ), 3, schema_example_quantized_position_fields_, schema_example_quantized_position_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_quantized_rotation_fields_[] = { + { "x", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedRotation, x ), (uint32_t) sizeof( ( (QuantizedRotation *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -1024.0, 1024.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedRotation, y ), (uint32_t) sizeof( ( (QuantizedRotation *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -1024.0, 1024.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedRotation, z ), (uint32_t) sizeof( ( (QuantizedRotation *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -1024.0, 1024.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "w", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedRotation, w ), (uint32_t) sizeof( ( (QuantizedRotation *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 1, -1024.0, 1024.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_quantized_rotation_info_ = { "QuantizedRotation", (uint32_t) sizeof( QuantizedRotation ), 4, schema_example_quantized_rotation_fields_, schema_example_quantized_rotation_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_quantized_velocity_fields_[] = { + { "x", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedVelocity, x ), (uint32_t) sizeof( ( (QuantizedVelocity *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.097152e+06, 2.097152e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedVelocity, y ), (uint32_t) sizeof( ( (QuantizedVelocity *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.097152e+06, 2.097152e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedVelocity, z ), (uint32_t) sizeof( ( (QuantizedVelocity *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.097152e+06, 2.097152e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_quantized_velocity_info_ = { "QuantizedVelocity", (uint32_t) sizeof( QuantizedVelocity ), 3, schema_example_quantized_velocity_fields_, schema_example_quantized_velocity_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_quat_fields_[] = { + { "x", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Quat, x ), (uint32_t) sizeof( ( (Quat *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Quat, y ), (uint32_t) sizeof( ( (Quat *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Quat, z ), (uint32_t) sizeof( ( (Quat *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "w", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Quat, w ), (uint32_t) sizeof( ( (Quat *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_quat_info_ = { "Quat", (uint32_t) sizeof( Quat ), 4, schema_example_quat_fields_, schema_example_quat_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_regain_after_align_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, lead ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "uint16", 0x0000000000000000ull, 7, 1, 1, 0, 3, (uint32_t) offsetof( RegainAfterAlign, items ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->items[0] ), (uint32_t) offsetof( RegainAfterAlign, items_count ), 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "s", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 4, (uint32_t) offsetof( RegainAfterAlign, s ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->s ), (uint32_t) offsetof( RegainAfterAlign, s_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "p", NULL, "bits(32)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, p ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->p ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "q", NULL, "bits(29)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, q ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->q ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 5.36870911e+08, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "r", NULL, "bits(19)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, r ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->r ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 524287.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(4)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, tail ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 15.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_regain_after_align_info_ = { "RegainAfterAlign", (uint32_t) sizeof( RegainAfterAlign ), 7, schema_example_regain_after_align_fields_, schema_example_regain_after_align_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_render_block_fields_[] = { + { "worker_index", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderBlock, worker_index ), (uint32_t) sizeof( ( (RenderBlock *) 0 )->worker_index ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "sprite_count_hint", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderBlock, sprite_count_hint ), (uint32_t) sizeof( ( (RenderBlock *) 0 )->sprite_count_hint ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "sprites", NULL, "RenderSprite", 0x0000000000000000ull, 13, 1, 1, 0, 64, (uint32_t) offsetof( RenderBlock, sprites ), (uint32_t) sizeof( ( (RenderBlock *) 0 )->sprites[0] ), (uint32_t) offsetof( RenderBlock, sprites_count ), 0xffffffffu, &schema_example_render_sprite_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_render_block_info_ = { "RenderBlock", (uint32_t) sizeof( RenderBlock ), 3, schema_example_render_block_fields_, schema_example_render_block_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_render_sprite_team_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Red", 0x0000000000000000ull }, + { "Blue", 0x0000000000000000ull }, +}; +static const TableFieldInfo schema_example_render_sprite_fields_[] = { + { "sort_key", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, sort_key ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->sort_key ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "mesh_id", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, mesh_id ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->mesh_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "material_id", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, material_id ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->material_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "layer", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, layer ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->layer ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", NULL, "Team", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, team ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_render_sprite_team_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_render_sprite_info_ = { "RenderSprite", (uint32_t) sizeof( RenderSprite ), 5, schema_example_render_sprite_fields_, schema_example_render_sprite_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_rigid_body_fields_[] = { + { "position", NULL, "Vec3", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, position ), (uint32_t) sizeof( ( (RigidBody *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_example_vec3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "orientation", NULL, "Quat", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, orientation ), (uint32_t) sizeof( ( (RigidBody *) 0 )->orientation ), 0xffffffffu, 0xffffffffu, &schema_example_quat_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "at_rest", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, at_rest ), (uint32_t) sizeof( ( (RigidBody *) 0 )->at_rest ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "linear_velocity", NULL, "Vec3", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, linear_velocity ), (uint32_t) sizeof( ( (RigidBody *) 0 )->linear_velocity ), 0xffffffffu, 0xffffffffu, &schema_example_vec3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!at_rest", TableDocNone, 0, NULL, 0, NULL }, + { "angular_velocity", NULL, "Vec3", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, angular_velocity ), (uint32_t) sizeof( ( (RigidBody *) 0 )->angular_velocity ), 0xffffffffu, 0xffffffffu, &schema_example_vec3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!at_rest", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_rigid_body_info_ = { "RigidBody", (uint32_t) sizeof( RigidBody ), 5, schema_example_rigid_body_fields_, schema_example_rigid_body_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_ship_create_ship_type_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Fighter", 0x0000000000000000ull }, + { "Corvette", 0x0000000000000000ull }, + { "Bomber", 0x0000000000000000ull }, + { "Destroyer", 0x0000000000000000ull }, + { "Carrier", 0x0000000000000000ull }, +}; +static const TableVariantInfo schema_example_ship_create_flags_outside_variants_[] = { + { "FiringLaser", 0 }, + { "Boosting", 0 }, + { "Braking", 0 }, + { "Aiming", 0 }, +}; +static const TableVariantInfo schema_example_ship_create_team_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Red", 0x0000000000000000ull }, + { "Blue", 0x0000000000000000ull }, +}; +static const TableVariantInfo schema_example_ship_create_pending_outside_variants_[] = { + { "None", 0x0000000000000000ull }, +}; +static const TableFieldInfo schema_example_ship_create_fields_[] = { + { "ship_type", NULL, "ShipType", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, ship_type ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->ship_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 5, schema_example_ship_create_ship_type_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "position", NULL, "QuantizedPosition", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, position ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_example_quantized_position_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", NULL, "QuantizedRotation", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, rotation ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_example_quantized_rotation_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "linear_velocity", NULL, "QuantizedVelocity", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, linear_velocity ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->linear_velocity ), 0xffffffffu, 0xffffffffu, &schema_example_quantized_velocity_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_flags", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, has_flags ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->has_flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", NULL, "ShipFlags", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, flags ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_example_ship_create_flags_outside_variants_, 0, NULL, NULL, -1, NULL, "has_flags", TableDocNone, 0, NULL, 0, NULL }, + { "team", NULL, "Team", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, team ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_ship_create_team_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "health", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, health ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "thrust", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, thrust ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->thrust ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pending", NULL, "Pending", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, pending ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->pending ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 0, schema_example_ship_create_pending_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_ship_create_info_ = { "ShipCreate", (uint32_t) sizeof( ShipCreate ), 10, schema_example_ship_create_fields_, schema_example_ship_create_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_sole_fields_[] = { + { "only", NULL, "bits(13)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( Sole, only ), (uint32_t) sizeof( ( (Sole *) 0 )->only ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_sole_info_ = { "Sole", (uint32_t) sizeof( Sole ), 1, schema_example_sole_fields_, schema_example_sole_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_chunk_fields_[] = { + { "values", NULL, "uint16", 0x0000000000000000ull, 7, 1, 0, 0, 4, (uint32_t) offsetof( SpanChunk, values ), (uint32_t) sizeof( ( (SpanChunk *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_chunk_info_ = { "SpanChunk", (uint32_t) sizeof( SpanChunk ), 1, schema_example_span_chunk_fields_, schema_example_span_chunk_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_f64_fields_[] = { + { "values", NULL, "float64", 0x0000000000000000ull, 11, 1, 0, 0, 2, (uint32_t) offsetof( SpanF64, values ), (uint32_t) sizeof( ( (SpanF64 *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_f64_info_ = { "SpanF64", (uint32_t) sizeof( SpanF64 ), 1, schema_example_span_f64_fields_, schema_example_span_f64_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_i64_fields_[] = { + { "values", NULL, "int64", 0x0000000000000000ull, 5, 1, 0, 0, 2, (uint32_t) offsetof( SpanI64, values ), (uint32_t) sizeof( ( (SpanI64 *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_i64_info_ = { "SpanI64", (uint32_t) sizeof( SpanI64 ), 1, schema_example_span_i64_fields_, schema_example_span_i64_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_one_fields_[] = { + { "values", NULL, "uint64", 0x0000000000000000ull, 9, 1, 0, 0, 1, (uint32_t) offsetof( SpanOne, values ), (uint32_t) sizeof( ( (SpanOne *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_one_info_ = { "SpanOne", (uint32_t) sizeof( SpanOne ), 1, schema_example_span_one_fields_, schema_example_span_one_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_tail_fields_[] = { + { "values", NULL, "float64", 0x0000000000000000ull, 11, 1, 0, 0, 2, (uint32_t) offsetof( SpanTail, values ), (uint32_t) sizeof( ( (SpanTail *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( SpanTail, tail ), (uint32_t) sizeof( ( (SpanTail *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_tail_info_ = { "SpanTail", (uint32_t) sizeof( SpanTail ), 2, schema_example_span_tail_fields_, schema_example_span_tail_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_twice_fields_[] = { + { "a", NULL, "float64", 0x0000000000000000ull, 11, 1, 0, 0, 2, (uint32_t) offsetof( SpanTwice, a ), (uint32_t) sizeof( ( (SpanTwice *) 0 )->a[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "float64", 0x0000000000000000ull, 11, 1, 0, 0, 2, (uint32_t) offsetof( SpanTwice, b ), (uint32_t) sizeof( ( (SpanTwice *) 0 )->b[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_twice_info_ = { "SpanTwice", (uint32_t) sizeof( SpanTwice ), 2, schema_example_span_twice_fields_, schema_example_span_twice_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_u64_fields_[] = { + { "values", NULL, "uint64", 0x0000000000000000ull, 9, 1, 0, 0, 2, (uint32_t) offsetof( SpanU64, values ), (uint32_t) sizeof( ( (SpanU64 *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_u64_info_ = { "SpanU64", (uint32_t) sizeof( SpanU64 ), 1, schema_example_span_u64_fields_, schema_example_span_u64_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_strs_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Strs, lead ), (uint32_t) sizeof( ( (Strs *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "s", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 8, (uint32_t) offsetof( Strs, s ), (uint32_t) sizeof( ( (Strs *) 0 )->s ), (uint32_t) offsetof( Strs, s_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bytes", 0x0000000000000000ull, 6, 1, 1, 0, 8, (uint32_t) offsetof( Strs, b ), (uint32_t) sizeof( ( (Strs *) 0 )->b[0] ), (uint32_t) offsetof( Strs, b_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Strs, tail ), (uint32_t) sizeof( ( (Strs *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_strs_info_ = { "Strs", (uint32_t) sizeof( Strs ), 4, schema_example_strs_fields_, schema_example_strs_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_test_fields_[] = { + { "test_a", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( Test, test_a ), (uint32_t) sizeof( ( (Test *) 0 )->test_a ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "test_b", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( Test, test_b ), (uint32_t) sizeof( ( (Test *) 0 )->test_b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "test_c", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( Test, test_c ), (uint32_t) sizeof( ( (Test *) 0 )->test_c ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "test_d", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( Test, test_d ), (uint32_t) sizeof( ( (Test *) 0 )->test_d ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_test_info_ = { "Test", (uint32_t) sizeof( Test ), 4, schema_example_test_fields_, schema_example_test_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_test_data_fields_[] = { + { "a", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TestData, a ), (uint32_t) sizeof( ( (TestData *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TestData, b ), (uint32_t) sizeof( ( (TestData *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "c", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TestData, c ), (uint32_t) sizeof( ( (TestData *) 0 )->c ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 150.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "d", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TestData, d ), (uint32_t) sizeof( ( (TestData *) 0 )->d ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "e", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TestData, e ), (uint32_t) sizeof( ( (TestData *) 0 )->e ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TestData, f ), (uint32_t) sizeof( ( (TestData *) 0 )->f ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "g", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( TestData, g ), (uint32_t) sizeof( ( (TestData *) 0 )->g ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "int32", 0x0000000000000000ull, 4, 1, 1, 0, 16, (uint32_t) offsetof( TestData, items ), (uint32_t) sizeof( ( (TestData *) 0 )->items[0] ), (uint32_t) offsetof( TestData, items_count ), 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "float_value", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TestData, float_value ), (uint32_t) sizeof( ( (TestData *) 0 )->float_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "compressed_float_value", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TestData, compressed_float_value ), (uint32_t) sizeof( ( (TestData *) 0 )->compressed_float_value ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "double_value", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( TestData, double_value ), (uint32_t) sizeof( ( (TestData *) 0 )->double_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "int8_value", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( TestData, int8_value ), (uint32_t) sizeof( ( (TestData *) 0 )->int8_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "int16_value", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( TestData, int16_value ), (uint32_t) sizeof( ( (TestData *) 0 )->int16_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "uint8_value", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TestData, uint8_value ), (uint32_t) sizeof( ( (TestData *) 0 )->uint8_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "uint16_value", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TestData, uint16_value ), (uint32_t) sizeof( ( (TestData *) 0 )->uint16_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "uint32_value", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( TestData, uint32_value ), (uint32_t) sizeof( ( (TestData *) 0 )->uint32_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "uint64_value", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TestData, uint64_value ), (uint32_t) sizeof( ( (TestData *) 0 )->uint64_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "int64_full", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( TestData, int64_full ), (uint32_t) sizeof( ( (TestData *) 0 )->int64_full ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "int64_range", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( TestData, int64_range ), (uint32_t) sizeof( ( (TestData *) 0 )->int64_range ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+12, 1e+12, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "fixed_bytes", NULL, "uint8", 0x0000000000000000ull, 6, 1, 0, 0, 17, (uint32_t) offsetof( TestData, fixed_bytes ), (uint32_t) sizeof( ( (TestData *) 0 )->fixed_bytes[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "text", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 255, (uint32_t) offsetof( TestData, text ), (uint32_t) sizeof( ( (TestData *) 0 )->text ), (uint32_t) offsetof( TestData, text_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_test_data_info_ = { "TestData", (uint32_t) sizeof( TestData ), 21, schema_example_test_data_fields_, schema_example_test_data_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_tri3_fields_[] = { + { "a", NULL, "bits(1)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Tri3, a ), (uint32_t) sizeof( ( (Tri3 *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(2)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Tri3, b ), (uint32_t) sizeof( ( (Tri3 *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_tri3_info_ = { "Tri3", (uint32_t) sizeof( Tri3 ), 2, schema_example_tri3_fields_, schema_example_tri3_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_trio_fields_[] = { + { "a", NULL, "bits(20)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( Trio, a ), (uint32_t) sizeof( ( (Trio *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.048575e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(20)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( Trio, b ), (uint32_t) sizeof( ( (Trio *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.048575e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "c", NULL, "bits(24)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( Trio, c ), (uint32_t) sizeof( ( (Trio *) 0 )->c ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.6777215e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_trio_info_ = { "Trio", (uint32_t) sizeof( Trio ), 3, schema_example_trio_fields_, schema_example_trio_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_trio_first_fields_[] = { + { "inner", NULL, "Trio", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( TrioFirst, inner ), (uint32_t) sizeof( ( (TrioFirst *) 0 )->inner ), 0xffffffffu, 0xffffffffu, &schema_example_trio_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "trailer", NULL, "bits(16)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TrioFirst, trailer ), (uint32_t) sizeof( ( (TrioFirst *) 0 )->trailer ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_trio_first_info_ = { "TrioFirst", (uint32_t) sizeof( TrioFirst ), 2, schema_example_trio_first_fields_, schema_example_trio_first_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_trio_sole_fields_[] = { + { "inner", NULL, "Trio", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( TrioSole, inner ), (uint32_t) sizeof( ( (TrioSole *) 0 )->inner ), 0xffffffffu, 0xffffffffu, &schema_example_trio_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_trio_sole_info_ = { "TrioSole", (uint32_t) sizeof( TrioSole ), 1, schema_example_trio_sole_fields_, schema_example_trio_sole_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_trio_straddle_fields_[] = { + { "pad0", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad0 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad0 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad1", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad1 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad1 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad2", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad2 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad2 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad3", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad3 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad3 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad4", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad4 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad4 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad5", NULL, "bits(24)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad5 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad5 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.6777215e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "inner", NULL, "Trio", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, inner ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->inner ), 0xffffffffu, 0xffffffffu, &schema_example_trio_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_trio_straddle_info_ = { "TrioStraddle", (uint32_t) sizeof( TrioStraddle ), 7, schema_example_trio_straddle_fields_, schema_example_trio_straddle_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_vec2_fields_[] = { + { "x", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec2, x ), (uint32_t) sizeof( ( (Vec2 *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec2, y ), (uint32_t) sizeof( ( (Vec2 *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_vec2_info_ = { "Vec2", (uint32_t) sizeof( Vec2 ), 2, schema_example_vec2_fields_, schema_example_vec2_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_vec3_fields_[] = { + { "x", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec3, x ), (uint32_t) sizeof( ( (Vec3 *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec3, y ), (uint32_t) sizeof( ( (Vec3 *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec3, z ), (uint32_t) sizeof( ( (Vec3 *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_vec3_info_ = { "Vec3", (uint32_t) sizeof( Vec3 ), 3, schema_example_vec3_fields_, schema_example_vec3_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w1_fields_[] = { + { "items", NULL, "uint8", 0x0000000000000000ull, 6, 1, 1, 0, 20, (uint32_t) offsetof( W1, items ), (uint32_t) sizeof( ( (W1 *) 0 )->items[0] ), (uint32_t) offsetof( W1, items_count ), 0xffffffffu, NULL, 1, 0.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w1_info_ = { "W1", (uint32_t) sizeof( W1 ), 1, schema_example_w1_fields_, schema_example_w1_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w13_fields_[] = { + { "items", NULL, "uint16", 0x0000000000000000ull, 7, 1, 1, 0, 12, (uint32_t) offsetof( W13, items ), (uint32_t) sizeof( ( (W13 *) 0 )->items[0] ), (uint32_t) offsetof( W13, items_count ), 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w13_info_ = { "W13", (uint32_t) sizeof( W13 ), 1, schema_example_w13_fields_, schema_example_w13_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w17_fields_[] = { + { "items", NULL, "uint32", 0x0000000000000000ull, 8, 1, 1, 0, 9, (uint32_t) offsetof( W17, items ), (uint32_t) sizeof( ( (W17 *) 0 )->items[0] ), (uint32_t) offsetof( W17, items_count ), 0xffffffffu, NULL, 1, 0.0, 131071.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w17_info_ = { "W17", (uint32_t) sizeof( W17 ), 1, schema_example_w17_fields_, schema_example_w17_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w26_fields_[] = { + { "items", NULL, "uint32", 0x0000000000000000ull, 8, 1, 1, 0, 6, (uint32_t) offsetof( W26, items ), (uint32_t) sizeof( ( (W26 *) 0 )->items[0] ), (uint32_t) offsetof( W26, items_count ), 0xffffffffu, NULL, 1, 0.0, 6.7108863e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w26_info_ = { "W26", (uint32_t) sizeof( W26 ), 1, schema_example_w26_fields_, schema_example_w26_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w50_fields_[] = { + { "items", NULL, "uint64", 0x0000000000000000ull, 9, 1, 1, 0, 3, (uint32_t) offsetof( W50, items ), (uint32_t) sizeof( ( (W50 *) 0 )->items[0] ), (uint32_t) offsetof( W50, items_count ), 0xffffffffu, NULL, 1, 0.0, 1.125899906842623e+15, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w50_info_ = { "W50", (uint32_t) sizeof( W50 ), 1, schema_example_w50_fields_, schema_example_w50_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w52_fields_[] = { + { "items", NULL, "uint64", 0x0000000000000000ull, 9, 1, 1, 0, 3, (uint32_t) offsetof( W52, items ), (uint32_t) sizeof( ( (W52 *) 0 )->items[0] ), (uint32_t) offsetof( W52, items_count ), 0xffffffffu, NULL, 1, 0.0, 4.503599627370495e+15, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w52_info_ = { "W52", (uint32_t) sizeof( W52 ), 1, schema_example_w52_fields_, schema_example_w52_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_wide_fields_[] = { + { "w", NULL, "bits(37)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( Wide, w ), (uint32_t) sizeof( ( (Wide *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.37438953471e+11, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_wide_info_ = { "Wide", (uint32_t) sizeof( Wide ), 1, schema_example_wide_fields_, schema_example_wide_reset_raw_, TableDocNone, 0, NULL }; + +const UnitViewInfo * schema_example_unit_view_(void) +{ + static const ViewVariant MissileType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Heatseeker", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Torpedo", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Nuke", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Pending_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant PropType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Asteroid", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Chunk", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Fragment", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Sphere", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "BlackHole", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 6, "DysonPanel", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ShipType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Fighter", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Corvette", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Bomber", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Destroyer", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "Carrier", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Team_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Red", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Blue", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Weapon_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Laser", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Missile", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Railgun", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ProbeFlags_view_variants[] = { + { 0, "Armed", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Cloaked", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Damaged", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ShipFlags_view_variants[] = { + { 0, "FiringLaser", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Boosting", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Braking", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Aiming", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant DefaultBulkChoice_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "first", 0x0000000000000000ull, "DefaultBulkArm", &schema_example_default_bulk_arm_info_, NULL, TableDocNone, 0, NULL }, + { 2, "second", 0x0000000000000000ull, "DefaultBulkArm", &schema_example_default_bulk_arm_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant DefaultChoice_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "first", 0x0000000000000000ull, "DefaultArm", &schema_example_default_arm_info_, NULL, TableDocNone, 0, NULL }, + { 2, "second", 0x0000000000000000ull, "DefaultArm", &schema_example_default_arm_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant EmptyUnion_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "a", 0x0000000000000000ull, "EmptyA", &schema_example_empty_a_info_, NULL, TableDocNone, 0, NULL }, + { 2, "b", 0x0000000000000000ull, "EmptyB", &schema_example_empty_b_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ProbeShape_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "ring", 0x0000000000000000ull, "ProbeRing", &schema_example_probe_ring_info_, NULL, TableDocNone, 0, NULL }, + { 2, "slab", 0x0000000000000000ull, "ProbeSlab", &schema_example_probe_slab_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Uneven_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "narrow", 0x0000000000000000ull, "Narrow", &schema_example_narrow_info_, NULL, TableDocNone, 0, NULL }, + { 2, "wide", 0x0000000000000000ull, "Wide", &schema_example_wide_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "MissileType", "Enums.schema", 3, 8, 4, MissileType_view_variants, TableDocNone, 0, NULL }, + { "Pending", "Enums.schema", 0, 8, 1, Pending_view_variants, TableDocNone, 0, NULL }, + { "PropType", "Enums.schema", 6, 8, 7, PropType_view_variants, TableDocNone, 0, NULL }, + { "ShipType", "Enums.schema", 5, 8, 6, ShipType_view_variants, TableDocNone, 0, NULL }, + { "Team", "Enums.schema", 2, 8, 3, Team_view_variants, TableDocNone, 0, NULL }, + { "Weapon", "Wire.schema", 15, 8, 4, Weapon_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary flags_decls[] = { + { "ProbeFlags", "Wire.schema", 2, 8, 3, ProbeFlags_view_variants, TableDocNone, 0, NULL }, + { "ShipFlags", "Enums.schema", 3, 8, 4, ShipFlags_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary unions[] = { + { "DefaultBulkChoice", "ArmDefaults.schema", 2, 8, 3, DefaultBulkChoice_view_variants, TableDocNone, 0, NULL }, + { "DefaultChoice", "ArmDefaults.schema", 2, 8, 3, DefaultChoice_view_variants, TableDocNone, 0, NULL }, + { "EmptyUnion", "Clauses.schema", 2, 8, 3, EmptyUnion_view_variants, TableDocNone, 0, NULL }, + { "ProbeShape", "Wire.schema", 2, 8, 3, ProbeShape_view_variants, TableDocNone, 0, NULL }, + { "Uneven", "Joins.schema", 2, 8, 3, Uneven_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "ArmAlign", "Joins.schema", 0, &schema_example_arm_align_info_, TableDocNone, 0, NULL }, + { "ArmArray", "Joins.schema", 0, &schema_example_arm_array_info_, TableDocNone, 0, NULL }, + { "ArmEmpty", "Joins.schema", 0, &schema_example_arm_empty_info_, TableDocNone, 0, NULL }, + { "ArmsAgree", "Joins.schema", 0, &schema_example_arms_agree_info_, TableDocNone, 0, NULL }, + { "ArmsDisagree", "Joins.schema", 0, &schema_example_arms_disagree_info_, TableDocNone, 0, NULL }, + { "ArmsNested", "Joins.schema", 0, &schema_example_arms_nested_info_, TableDocNone, 0, NULL }, + { "ArrEleven", "Clauses.schema", 0, &schema_example_arr_eleven_info_, TableDocNone, 0, NULL }, + { "ArrNested", "Clauses.schema", 0, &schema_example_arr_nested_info_, TableDocNone, 0, NULL }, + { "ArrTri3", "Clauses.schema", 0, &schema_example_arr_tri3_info_, TableDocNone, 0, NULL }, + { "ArrUneven", "Joins.schema", 0, &schema_example_arr_uneven_info_, TableDocNone, 0, NULL }, + { "Block", "Wire.schema", 0, &schema_example_block_info_, TableDocNone, 0, NULL }, + { "Chat", "Wire.schema", 0, &schema_example_chat_info_, TableDocNone, 0, NULL }, + { "CompressedProbe", "Wire.schema", 0, &schema_example_compressed_probe_info_, TableDocNone, 0, NULL }, + { "DefaultArm", "ArmDefaults.schema", 0, &schema_example_default_arm_info_, TableDocNone, 0, NULL }, + { "DefaultBulkArm", "ArmDefaults.schema", 0, &schema_example_default_bulk_arm_info_, TableDocNone, 0, NULL }, + { "DefaultChoicePacket", "ArmDefaults.schema", 0, &schema_example_default_choice_packet_info_, TableDocNone, 0, NULL }, + { "Eleven", "Clauses.schema", 0, &schema_example_eleven_info_, TableDocNone, 0, NULL }, + { "EmptyA", "Clauses.schema", 0, &schema_example_empty_a_info_, TableDocNone, 0, NULL }, + { "EmptyB", "Clauses.schema", 0, &schema_example_empty_b_info_, TableDocNone, 0, NULL }, + { "ExpressionProbe", "Types.schema", 0, &schema_example_expression_probe_info_, TableDocNone, 0, NULL }, + { "ExtremeProbe", "Types.schema", 0, &schema_example_extreme_probe_info_, TableDocNone, 0, NULL }, + { "ExtremeRow", "Types.schema", 0, &schema_example_extreme_row_info_, TableDocNone, 0, NULL }, + { "F13", "Clauses.schema", 0, &schema_example_f13_info_, TableDocNone, 0, NULL }, + { "Handle", "Types.schema", 0, &schema_example_handle_info_, TableDocNone, 0, NULL }, + { "Heartbeat", "Wire.schema", 0, &schema_example_heartbeat_info_, TableDocNone, 0, NULL }, + { "HoldsEmptyUnion", "Clauses.schema", 0, &schema_example_holds_empty_union_info_, TableDocNone, 0, NULL }, + { "HoldsUneven", "Joins.schema", 0, &schema_example_holds_uneven_info_, TableDocNone, 0, NULL }, + { "Input", "Types.schema", 0, &schema_example_input_info_, TableDocNone, 0, NULL }, + { "InputPacket", "Types.schema", 0, &schema_example_input_packet_info_, TableDocNone, 0, NULL }, + { "Narrow", "Joins.schema", 0, &schema_example_narrow_info_, TableDocNone, 0, NULL }, + { "ProbeArray", "Wire.schema", 0, &schema_example_probe_array_info_, TableDocNone, 0, NULL }, + { "ProbeBits", "Wire.schema", 0, &schema_example_probe_bits_info_, TableDocNone, 0, NULL }, + { "ProbeCollider", "Wire.schema", 0, &schema_example_probe_collider_info_, TableDocNone, 0, NULL }, + { "ProbeConfig", "Wire.schema", 0, &schema_example_probe_config_info_, TableDocNone, 0, NULL }, + { "ProbeHeader", "Wire.schema", 0, &schema_example_probe_header_info_, TableDocNone, 0, NULL }, + { "ProbeReport", "Wire.schema", 0, &schema_example_probe_report_info_, TableDocNone, 0, NULL }, + { "ProbeRing", "Wire.schema", 0, &schema_example_probe_ring_info_, TableDocNone, 0, NULL }, + { "ProbeSample", "Wire.schema", 0, &schema_example_probe_sample_info_, TableDocNone, 0, NULL }, + { "ProbeSlab", "Wire.schema", 0, &schema_example_probe_slab_info_, TableDocNone, 0, NULL }, + { "QuantizedPosition", "Types.schema", 0, &schema_example_quantized_position_info_, TableDocNone, 0, NULL }, + { "QuantizedRotation", "Types.schema", 0, &schema_example_quantized_rotation_info_, TableDocNone, 0, NULL }, + { "QuantizedVelocity", "Types.schema", 0, &schema_example_quantized_velocity_info_, TableDocNone, 0, NULL }, + { "Quat", "Types.schema", 0, &schema_example_quat_info_, TableDocNone, 0, NULL }, + { "RegainAfterAlign", "Joins.schema", 0, &schema_example_regain_after_align_info_, TableDocNone, 0, NULL }, + { "RenderBlock", "Render.schema", 0, &schema_example_render_block_info_, TableDocNone, 0, NULL }, + { "RenderSprite", "Render.schema", 0, &schema_example_render_sprite_info_, TableDocNone, 0, NULL }, + { "RigidBody", "Types.schema", 0, &schema_example_rigid_body_info_, TableDocNone, 0, NULL }, + { "ShipCreate", "Types.schema", 0, &schema_example_ship_create_info_, TableDocNone, 0, NULL }, + { "Sole", "Clauses.schema", 0, &schema_example_sole_info_, TableDocNone, 0, NULL }, + { "SpanChunk", "Degenerate.schema", 0, &schema_example_span_chunk_info_, TableDocNone, 0, NULL }, + { "SpanF64", "Degenerate.schema", 0, &schema_example_span_f64_info_, TableDocNone, 0, NULL }, + { "SpanI64", "Degenerate.schema", 0, &schema_example_span_i64_info_, TableDocNone, 0, NULL }, + { "SpanOne", "Degenerate.schema", 0, &schema_example_span_one_info_, TableDocNone, 0, NULL }, + { "SpanTail", "Degenerate.schema", 0, &schema_example_span_tail_info_, TableDocNone, 0, NULL }, + { "SpanTwice", "Degenerate.schema", 0, &schema_example_span_twice_info_, TableDocNone, 0, NULL }, + { "SpanU64", "Degenerate.schema", 0, &schema_example_span_u64_info_, TableDocNone, 0, NULL }, + { "Strs", "Clauses.schema", 0, &schema_example_strs_info_, TableDocNone, 0, NULL }, + { "Test", "Wire.schema", 0, &schema_example_test_info_, TableDocNone, 0, NULL }, + { "TestData", "Wire.schema", 0, &schema_example_test_data_info_, TableDocNone, 0, NULL }, + { "Tri3", "Clauses.schema", 0, &schema_example_tri3_info_, TableDocNone, 0, NULL }, + { "Trio", "Degenerate.schema", 0, &schema_example_trio_info_, TableDocNone, 0, NULL }, + { "TrioFirst", "Degenerate.schema", 0, &schema_example_trio_first_info_, TableDocNone, 0, NULL }, + { "TrioSole", "Degenerate.schema", 0, &schema_example_trio_sole_info_, TableDocNone, 0, NULL }, + { "TrioStraddle", "Degenerate.schema", 0, &schema_example_trio_straddle_info_, TableDocNone, 0, NULL }, + { "Vec2", "Degenerate.schema", 0, &schema_example_vec2_info_, TableDocNone, 0, NULL }, + { "Vec3", "Types.schema", 0, &schema_example_vec3_info_, TableDocNone, 0, NULL }, + { "W1", "Clauses.schema", 0, &schema_example_w1_info_, TableDocNone, 0, NULL }, + { "W13", "Clauses.schema", 0, &schema_example_w13_info_, TableDocNone, 0, NULL }, + { "W17", "Clauses.schema", 0, &schema_example_w17_info_, TableDocNone, 0, NULL }, + { "W26", "Clauses.schema", 0, &schema_example_w26_info_, TableDocNone, 0, NULL }, + { "W50", "Clauses.schema", 0, &schema_example_w50_info_, TableDocNone, 0, NULL }, + { "W52", "Clauses.schema", 0, &schema_example_w52_info_, TableDocNone, 0, NULL }, + { "Wide", "Joins.schema", 0, &schema_example_wide_info_, TableDocNone, 0, NULL }, + }; + static const ViewConstant constants[] = { + { "CeilingCount", "Types.schema", "uint64", 0, (int64_t) 0xffffffffffffffffull, 0.0, TableDocNone, 0, NULL }, + { "FloorLimit", "Types.schema", "int64", 0, (-INT64_C(9223372036854775807)-1), 0.0, TableDocNone, 0, NULL }, + { "HalfTurn", "Wire.schema", "float64", 1, 0, 180.0, TableDocNone, 0, NULL }, + { "MaxBlockSize", "Constants.schema", "int64", 0, INT64_C(2000), 0.0, TableDocNone, 0, NULL }, + { "MaxChatLength", "Constants.schema", "int64", 0, INT64_C(256), 0.0, TableDocNone, 0, NULL }, + { "MaxHealth", "Constants.schema", "int64", 0, INT64_C(1000), 0.0, TableDocNone, 0, NULL }, + { "MaxInputsPerPacket", "Constants.schema", "int64", 0, INT64_C(16), 0.0, TableDocNone, 0, NULL }, + { "MaxObjects", "Constants.schema", "int64", 0, INT64_C(10000), 0.0, TableDocNone, 0, NULL }, + { "MaxPositionUnits", "Constants.schema", "int64", 0, INT64_C(8388608), 0.0, TableDocNone, 0, NULL }, + { "MaxSpeedMeters", "Constants.schema", "int64", 0, INT64_C(2048), 0.0, TableDocNone, 0, NULL }, + { "MaxVelocityUnits", "Constants.schema", "int64", 0, INT64_C(2097152), 0.0, TableDocNone, 0, NULL }, + { "MaxWorldMeters", "Constants.schema", "int64", 0, INT64_C(8192), 0.0, TableDocNone, 0, NULL }, + { "NumTeams", "Constants.schema", "int64", 0, INT64_C(2), 0.0, TableDocNone, 0, NULL }, + { "PositionUnits", "Constants.schema", "int64", 0, INT64_C(1024), 0.0, TableDocNone, 0, NULL }, + { "ProbeId", "Wire.schema", "uint64", 0, INT64_C(244837814094590), 0.0, TableDocNone, 0, NULL }, + { "RenderBlockMaxSprites", "Render.schema", "int64", 0, INT64_C(64), 0.0, TableDocNone, 0, NULL }, + { "RotationUnits", "Constants.schema", "int64", 0, INT64_C(1024), 0.0, TableDocNone, 0, NULL }, + { "ShipMaxLasers", "Constants.schema", "int64", 0, INT64_C(16), 0.0, TableDocNone, 0, NULL }, + { "ShipMaxMissiles", "Constants.schema", "int64", 0, INT64_C(16), 0.0, TableDocNone, 0, NULL }, + { "SpinRate", "Wire.schema", "float64", 1, 0, 1.5, TableDocNone, 0, NULL }, + { "TickRate", "Wire.schema", "float32", 1, 0, 60.0, TableDocNone, 0, NULL }, + { "VelocityUnits", "Constants.schema", "int64", 0, INT64_C(1024), 0.0, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "example", 0x8656ae68c06b97a7ull, + 73, types, + 0, NULL, + 6, enums, + 2, flags_decls, + 5, unions, + 22, constants }; + return &info; +} diff --git a/generated/c/ExampleView.h b/generated/c/ExampleView.h new file mode 100644 index 000000000..634d2bc36 --- /dev/null +++ b/generated/c/ExampleView.h @@ -0,0 +1,1329 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_EXAMPLE_VIEW_H +#define SCHEMA_EXAMPLE_VIEW_H +#include +#include +#include +#include "ArmDefaults.h" +#include "Clauses.h" +#include "Constants.h" +#include "Degenerate.h" +#include "Enums.h" +#include "Joins.h" +#include "Render.h" +#include "Types.h" +#include "Wire.h" +#ifndef SCHEMA_EXAMPLE_TABLE_PRIMITIVES +#define SCHEMA_EXAMPLE_TABLE_PRIMITIVES + +/* THE CODEC DOES NOT DEPEND ON THE COMPILER'S INLINING BUDGET. A table of a + realistic field count emits one large body per type, and the cursor a body + writes through lives in the caller's TableWriter: across a call boundary that + cursor round-trips through memory, and a uint8_t * store may alias the writer + itself, so every put reloads it. When a budget runs out mid-body the codec + silently degrades to that shape. Forcing the primitives and the fixed-class + bodies inline is what keeps the cursor in registers and lets adjacent + constant framing bytes merge into one store. + + The spelling is FEATURE TESTED rather than assumed, exactly as the packet + emitter's own inlining demand is (SPEC §6.1's C column). + + IT CARRIES THE PACKAGE for the reason every other name here does, and that + reason is the LINKER rather than the preprocessor: two units of the same + schema family link into one program — two generations of one schema is the + case the conformance driver itself is — so nothing this backend emits may + be spelled the same way by two packages. They do NOT share a translation + unit and cannot: C has no namespace, so two units' headers in one TU + redefine TableReport, TableWriter and every other runtime name, which is the + packet emitter's standing limit (SPEC §6.1) and not something tables + changed. A per-package macro is what keeps the LINK legal. */ +#ifndef SCHEMA_EXAMPLE_TABLE_INLINE +#if defined( _MSC_VER ) +#define SCHEMA_EXAMPLE_TABLE_INLINE __forceinline +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_EXAMPLE_TABLE_INLINE inline __attribute__(( always_inline )) +#else +#define SCHEMA_EXAMPLE_TABLE_INLINE inline +#endif +#endif + +/* A COMPILE-TIME assertion under -std=c99, which has none of its own. C11's + _Static_assert carries the message into the build log verbatim and is used + wherever the compiler has it; everywhere else the negative array bound is + the same refusal with a tag a reader can find. The tag is unique per record + and field, and each record is asserted by the ONE header that declares it, + so no two of these ever meet. */ +#ifndef SCHEMA_TABLE_STATIC_ASSERT +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) _Static_assert( cond, message ) +#elif defined( __cplusplus ) && __cplusplus >= 201103L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) static_assert( cond, message ) +#else +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) typedef char schema_table_assert_##tag[ ( cond ) ? 1 : -1 ] +#endif +#endif + +/* A record's ALIGNMENT, which the cook's layout contract asserts and which the + cook's Open compares the header's alignment word against (§7.1, §20.3). + C99 has no alignof either; C11's _Alignof, the two GNU spellings and MSVC's + cover every compiler this repo builds under, and the last form is the + portable definition of the same number. */ +#ifndef SCHEMA_TABLE_ALIGNOF +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_ALIGNOF( T ) _Alignof( T ) +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof__( T ) +#elif defined( _MSC_VER ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof( T ) +#else +#define SCHEMA_TABLE_ALIGNOF( T ) ( (size_t) offsetof( struct { char schema_pad_; T schema_member_; }, schema_member_ ) ) +#endif +#endif + +/* The table-wire read report — the permissive contract's ledger. Silence + (all zero) means the data matched this reader's schema exactly. */ +typedef struct TableReport +{ + int32_t unknown; /* unknown field ids skipped (newer data) */ + int32_t kind_mismatch; /* known id, changed type — skipped, never misdecoded */ + int32_t clamped; /* out-of-range values clamped to declared bounds */ + /* a key the TEXT form saw twice: last wins, and the repeat is counted + (docs/SPEC-TABLES.md §16.2). The wire never raises it — a body carrying an + id twice is legal input whose last occurrence wins, silently (§3). */ + int32_t duplicate; + int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ +} TableReport; + +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + +/* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- + + Static field descriptors for every type in the table closure: name, wire + id/kind, storage offset, bounds, ranges, enum names and branch guards — + enough to walk, print, diff, edit or bind any table value at runtime with + no schema files. _table_type() returns 's descriptor. */ + +struct TableTypeInfo; +struct TableFieldInfo; + +/* One arm of a union field: where its payload sits inside the union's storage + and what its payload looks like. The arm's NAME and its table-wire id ride + in the field's own vocabulary table at the same tag, so nothing is spelled + twice (docs/SPEC-TABLES.md §8). */ +typedef struct TableUnionArmInfo +{ + uint32_t offset; /* offsetof the arm's payload within the union storage */ + const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ +} TableUnionArmInfo; + +/* A union field's shape: the tag, and the arms indexed by it. Arms run + [0, enum_max]; index 0 is the EMPTY arm and carries no payload. */ +typedef struct TableUnionInfo +{ + uint32_t tag_offset; /* offsetof the tag within the union storage */ + uint32_t tag_size; /* sizeof the tag */ + const TableUnionArmInfo * arms; +} TableUnionInfo; + +/* ONE ENTRY OF A VOCABULARY, and the whole of what C++ spells as a pair of + captureless lambdas. An enum's values, a union's arms and a flags field's + BITS are each a named set indexed by [0, enum_max], so the set rides as an + ARRAY indexed the same way: no function per enum, and therefore no name per + enum for a schema to collide with (docs/SPEC-TABLES.md §11). + + name is the variant's spelling, or NULL for a value the declared set does + not name. id is the TABLE-WIRE id it rides under (§5) — the hash of the + variant's name, 0 for the reserved None and for the empty union arm. A + FLAGS field's variants are BIT POSITIONS and have no per-variant wire id + (§4), so has_ids is 0 there and 1 for an enum and a union: a vocabulary + with names and no ids is what says "flags". */ +typedef struct TableVariantInfo +{ + const char * name; + uint64_t id; +} TableVariantInfo; + +/* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// + block carries a doc column pointing at this one object, so absence costs a + unit no string data and a printer concatenates doc columns with no null + test. One definition per translation unit, and the descriptors of a unit + are one, so every absent doc compares equal by address. */ +static SCHEMA_UNUSED const char TableDocNone[1] = ""; + +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + +typedef struct TableFieldInfo +{ + const char * name; /* schema field name, e.g. "health" */ + const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ + const char * type_name; /* schema type name, e.g. "float32", "Grade" */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ + int is_array; /* fixed or counted array (bytes included) */ + int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ + int optional; /* a ?T field: a _present companion decides whether it rides */ + int32_t array_bound; /* array capacity / string max length; 0 for plain scalars */ + uint32_t offset; /* offsetof the storage member */ + uint32_t elem_size; /* sizeof the member (element size for arrays) */ + uint32_t count_offset; /* offsetof the _count/_length companion, or 0xffffffff */ + uint32_t present_offset; /* offsetof the _present companion, or 0xffffffff */ + const struct TableTypeInfo * table; /* nested table's descriptor, or NULL */ + int has_range; /* a declared [min, max] (int or float) */ + double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ + double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; + int64_t enum_max; /* enums: highest valid value (None = 0 always valid); + unions: the arm count (tag range [0, enum_max]); + flags: the highest declared BIT INDEX; else -1 */ + /* the vocabulary, indexed the same way enum_max bounds: an enum's value -> + (name, id), a union's tag -> (arm name, arm id), a FLAGS field's bit + index -> (variant name, 0). NULL for every other kind. */ + const TableVariantInfo * variants; + int has_variant_ids; /* 1 for an enum or a union, 0 for flags (§4) */ + /* an ENUM-KEYED array (docs/SPEC-TABLES.md §2.4): the array has one slot per + variant of key_type_name, the key k at index k-1, and its slots ride + under variant ids rather than positions. keys is the key's vocabulary, + indexed by the KEY — walk [0, array_bound) and ask about index + 1 to + print slots by name. NULL on every other field. */ + const char * key_type_name; + const TableVariantInfo * keys; + int64_t key_max; /* the key enum's highest value, bounding keys */ + /* union fields: the tag and its arms. NULL for every other kind. */ + const TableUnionInfo * arms; + const char * guard; /* branch guard, e.g. "at_rest" or "!at_rest"; "" if unguarded */ + /* what a PERSON wrote about the field (docs/SPEC-TABLES.md §8.1): the /// + block above it, verbatim (SPEC §4.1). It is TableDocNone when there is + none, never NULL. Its tags (SPEC §4.2) follow in declared order, and an + untagged field is 0 beside NULL. Static, constant-initialized, + allocating nothing. */ + const char * doc; + int32_t num_tags; + const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); +} TableFieldInfo; + +typedef struct TableTypeInfo +{ + const char * name; /* schema type name */ + uint32_t size; /* sizeof the storage struct */ + int32_t num_fields; + const TableFieldInfo * fields; + /* put one instance back at its declared defaults, in place. A generic + walker that fills a value has to be able to establish the defaults an + absent field takes, and it holds no type to spell — this is the one + thing the descriptors could not express without it. It is the void * + form of _reset and the same code. */ + void (*reset)( void * storage ); + /* the declaration's own doc and tags, on the same terms as a field's + (docs/SPEC-TABLES.md §8.1) */ + const char * doc; + int32_t num_tags; + const char * const * tags; +} TableTypeInfo; + + + +static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } +static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } +static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } +static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } + +#endif /* SCHEMA_EXAMPLE_TABLE_PRIMITIVES */ + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void arm_align_reset( ArmAlign * value ); +static SCHEMA_UNUSED void schema_example_arm_align_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arm_array_reset( ArmArray * value ); +static SCHEMA_UNUSED void schema_example_arm_array_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arm_empty_reset( ArmEmpty * value ); +static SCHEMA_UNUSED void schema_example_arm_empty_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arms_agree_reset( ArmsAgree * value ); +static SCHEMA_UNUSED void schema_example_arms_agree_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arms_disagree_reset( ArmsDisagree * value ); +static SCHEMA_UNUSED void schema_example_arms_disagree_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arms_nested_reset( ArmsNested * value ); +static SCHEMA_UNUSED void schema_example_arms_nested_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arr_eleven_reset( ArrEleven * value ); +static SCHEMA_UNUSED void schema_example_arr_eleven_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arr_nested_reset( ArrNested * value ); +static SCHEMA_UNUSED void schema_example_arr_nested_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arr_tri3_reset( ArrTri3 * value ); +static SCHEMA_UNUSED void schema_example_arr_tri3_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arr_uneven_reset( ArrUneven * value ); +static SCHEMA_UNUSED void schema_example_arr_uneven_reset_raw_( void * storage ); +static SCHEMA_UNUSED void block_reset( Block * value ); +static SCHEMA_UNUSED void schema_example_block_reset_raw_( void * storage ); +static SCHEMA_UNUSED void chat_reset( Chat * value ); +static SCHEMA_UNUSED void schema_example_chat_reset_raw_( void * storage ); +static SCHEMA_UNUSED void compressed_probe_reset( CompressedProbe * value ); +static SCHEMA_UNUSED void schema_example_compressed_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void default_arm_reset( DefaultArm * value ); +static SCHEMA_UNUSED void schema_example_default_arm_reset_raw_( void * storage ); +static SCHEMA_UNUSED void default_bulk_arm_reset( DefaultBulkArm * value ); +static SCHEMA_UNUSED void schema_example_default_bulk_arm_reset_raw_( void * storage ); +static SCHEMA_UNUSED void default_choice_packet_reset( DefaultChoicePacket * value ); +static SCHEMA_UNUSED void schema_example_default_choice_packet_reset_raw_( void * storage ); +static SCHEMA_UNUSED void eleven_reset( Eleven * value ); +static SCHEMA_UNUSED void schema_example_eleven_reset_raw_( void * storage ); +static SCHEMA_UNUSED void empty_a_reset( EmptyA * value ); +static SCHEMA_UNUSED void schema_example_empty_a_reset_raw_( void * storage ); +static SCHEMA_UNUSED void empty_b_reset( EmptyB * value ); +static SCHEMA_UNUSED void schema_example_empty_b_reset_raw_( void * storage ); +static SCHEMA_UNUSED void expression_probe_reset( ExpressionProbe * value ); +static SCHEMA_UNUSED void schema_example_expression_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void extreme_probe_reset( ExtremeProbe * value ); +static SCHEMA_UNUSED void schema_example_extreme_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void extreme_row_reset( ExtremeRow * value ); +static SCHEMA_UNUSED void schema_example_extreme_row_reset_raw_( void * storage ); +static SCHEMA_UNUSED void f13_reset( F13 * value ); +static SCHEMA_UNUSED void schema_example_f13_reset_raw_( void * storage ); +static SCHEMA_UNUSED void handle_reset( Handle * value ); +static SCHEMA_UNUSED void schema_example_handle_reset_raw_( void * storage ); +static SCHEMA_UNUSED void heartbeat_reset( Heartbeat * value ); +static SCHEMA_UNUSED void schema_example_heartbeat_reset_raw_( void * storage ); +static SCHEMA_UNUSED void holds_empty_union_reset( HoldsEmptyUnion * value ); +static SCHEMA_UNUSED void schema_example_holds_empty_union_reset_raw_( void * storage ); +static SCHEMA_UNUSED void holds_uneven_reset( HoldsUneven * value ); +static SCHEMA_UNUSED void schema_example_holds_uneven_reset_raw_( void * storage ); +static SCHEMA_UNUSED void input_reset( Input * value ); +static SCHEMA_UNUSED void schema_example_input_reset_raw_( void * storage ); +static SCHEMA_UNUSED void input_packet_reset( InputPacket * value ); +static SCHEMA_UNUSED void schema_example_input_packet_reset_raw_( void * storage ); +static SCHEMA_UNUSED void narrow_reset( Narrow * value ); +static SCHEMA_UNUSED void schema_example_narrow_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_array_reset( ProbeArray * value ); +static SCHEMA_UNUSED void schema_example_probe_array_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_bits_reset( ProbeBits * value ); +static SCHEMA_UNUSED void schema_example_probe_bits_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_collider_reset( ProbeCollider * value ); +static SCHEMA_UNUSED void schema_example_probe_collider_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_config_reset( ProbeConfig * value ); +static SCHEMA_UNUSED void schema_example_probe_config_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_header_reset( ProbeHeader * value ); +static SCHEMA_UNUSED void schema_example_probe_header_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_report_reset( ProbeReport * value ); +static SCHEMA_UNUSED void schema_example_probe_report_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_ring_reset( ProbeRing * value ); +static SCHEMA_UNUSED void schema_example_probe_ring_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_sample_reset( ProbeSample * value ); +static SCHEMA_UNUSED void schema_example_probe_sample_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_slab_reset( ProbeSlab * value ); +static SCHEMA_UNUSED void schema_example_probe_slab_reset_raw_( void * storage ); +static SCHEMA_UNUSED void quantized_position_reset( QuantizedPosition * value ); +static SCHEMA_UNUSED void schema_example_quantized_position_reset_raw_( void * storage ); +static SCHEMA_UNUSED void quantized_rotation_reset( QuantizedRotation * value ); +static SCHEMA_UNUSED void schema_example_quantized_rotation_reset_raw_( void * storage ); +static SCHEMA_UNUSED void quantized_velocity_reset( QuantizedVelocity * value ); +static SCHEMA_UNUSED void schema_example_quantized_velocity_reset_raw_( void * storage ); +static SCHEMA_UNUSED void quat_reset( Quat * value ); +static SCHEMA_UNUSED void schema_example_quat_reset_raw_( void * storage ); +static SCHEMA_UNUSED void regain_after_align_reset( RegainAfterAlign * value ); +static SCHEMA_UNUSED void schema_example_regain_after_align_reset_raw_( void * storage ); +static SCHEMA_UNUSED void render_block_reset( RenderBlock * value ); +static SCHEMA_UNUSED void schema_example_render_block_reset_raw_( void * storage ); +static SCHEMA_UNUSED void render_sprite_reset( RenderSprite * value ); +static SCHEMA_UNUSED void schema_example_render_sprite_reset_raw_( void * storage ); +static SCHEMA_UNUSED void rigid_body_reset( RigidBody * value ); +static SCHEMA_UNUSED void schema_example_rigid_body_reset_raw_( void * storage ); +static SCHEMA_UNUSED void ship_create_reset( ShipCreate * value ); +static SCHEMA_UNUSED void schema_example_ship_create_reset_raw_( void * storage ); +static SCHEMA_UNUSED void sole_reset( Sole * value ); +static SCHEMA_UNUSED void schema_example_sole_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_chunk_reset( SpanChunk * value ); +static SCHEMA_UNUSED void schema_example_span_chunk_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_f64_reset( SpanF64 * value ); +static SCHEMA_UNUSED void schema_example_span_f64_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_i64_reset( SpanI64 * value ); +static SCHEMA_UNUSED void schema_example_span_i64_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_one_reset( SpanOne * value ); +static SCHEMA_UNUSED void schema_example_span_one_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_tail_reset( SpanTail * value ); +static SCHEMA_UNUSED void schema_example_span_tail_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_twice_reset( SpanTwice * value ); +static SCHEMA_UNUSED void schema_example_span_twice_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_u64_reset( SpanU64 * value ); +static SCHEMA_UNUSED void schema_example_span_u64_reset_raw_( void * storage ); +static SCHEMA_UNUSED void strs_reset( Strs * value ); +static SCHEMA_UNUSED void schema_example_strs_reset_raw_( void * storage ); +static SCHEMA_UNUSED void test_reset( Test * value ); +static SCHEMA_UNUSED void schema_example_test_reset_raw_( void * storage ); +static SCHEMA_UNUSED void test_data_reset( TestData * value ); +static SCHEMA_UNUSED void schema_example_test_data_reset_raw_( void * storage ); +static SCHEMA_UNUSED void tri3_reset( Tri3 * value ); +static SCHEMA_UNUSED void schema_example_tri3_reset_raw_( void * storage ); +static SCHEMA_UNUSED void trio_reset( Trio * value ); +static SCHEMA_UNUSED void schema_example_trio_reset_raw_( void * storage ); +static SCHEMA_UNUSED void trio_first_reset( TrioFirst * value ); +static SCHEMA_UNUSED void schema_example_trio_first_reset_raw_( void * storage ); +static SCHEMA_UNUSED void trio_sole_reset( TrioSole * value ); +static SCHEMA_UNUSED void schema_example_trio_sole_reset_raw_( void * storage ); +static SCHEMA_UNUSED void trio_straddle_reset( TrioStraddle * value ); +static SCHEMA_UNUSED void schema_example_trio_straddle_reset_raw_( void * storage ); +static SCHEMA_UNUSED void vec2_reset( Vec2 * value ); +static SCHEMA_UNUSED void schema_example_vec2_reset_raw_( void * storage ); +static SCHEMA_UNUSED void vec3_reset( Vec3 * value ); +static SCHEMA_UNUSED void schema_example_vec3_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w1_reset( W1 * value ); +static SCHEMA_UNUSED void schema_example_w1_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w13_reset( W13 * value ); +static SCHEMA_UNUSED void schema_example_w13_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w17_reset( W17 * value ); +static SCHEMA_UNUSED void schema_example_w17_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w26_reset( W26 * value ); +static SCHEMA_UNUSED void schema_example_w26_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w50_reset( W50 * value ); +static SCHEMA_UNUSED void schema_example_w50_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w52_reset( W52 * value ); +static SCHEMA_UNUSED void schema_example_w52_reset_raw_( void * storage ); +static SCHEMA_UNUSED void wide_reset( Wide * value ); +static SCHEMA_UNUSED void schema_example_wide_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void arm_align_reset( ArmAlign * value ) +{ + value->lead = 0; + value->flag = 0; + memset( value->s, 0, sizeof( value->s ) ); + value->s_length = 0; + value->b = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arm_align_reset_raw_( void * storage ) { arm_align_reset( (ArmAlign *) storage ); } + +extern const TableTypeInfo schema_example_arm_align_info_; +static SCHEMA_UNUSED const TableTypeInfo * arm_align_table_type(void) { return &schema_example_arm_align_info_; } +static SCHEMA_UNUSED void arm_array_reset( ArmArray * value ) +{ + value->lead = 0; + value->flag = 0; + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; + value->b = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arm_array_reset_raw_( void * storage ) { arm_array_reset( (ArmArray *) storage ); } + +extern const TableTypeInfo schema_example_arm_array_info_; +static SCHEMA_UNUSED const TableTypeInfo * arm_array_table_type(void) { return &schema_example_arm_array_info_; } +static SCHEMA_UNUSED void arm_empty_reset( ArmEmpty * value ) +{ + value->lead = 0; + value->flag = 0; + value->a = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arm_empty_reset_raw_( void * storage ) { arm_empty_reset( (ArmEmpty *) storage ); } + +extern const TableTypeInfo schema_example_arm_empty_info_; +static SCHEMA_UNUSED const TableTypeInfo * arm_empty_table_type(void) { return &schema_example_arm_empty_info_; } +static SCHEMA_UNUSED void arms_agree_reset( ArmsAgree * value ) +{ + value->lead = 0; + value->flag = 0; + value->a = 0; + value->b = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arms_agree_reset_raw_( void * storage ) { arms_agree_reset( (ArmsAgree *) storage ); } + +extern const TableTypeInfo schema_example_arms_agree_info_; +static SCHEMA_UNUSED const TableTypeInfo * arms_agree_table_type(void) { return &schema_example_arms_agree_info_; } +static SCHEMA_UNUSED void arms_disagree_reset( ArmsDisagree * value ) +{ + value->lead = 0; + value->flag = 0; + value->a = 0; + value->b = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arms_disagree_reset_raw_( void * storage ) { arms_disagree_reset( (ArmsDisagree *) storage ); } + +extern const TableTypeInfo schema_example_arms_disagree_info_; +static SCHEMA_UNUSED const TableTypeInfo * arms_disagree_table_type(void) { return &schema_example_arms_disagree_info_; } +static SCHEMA_UNUSED void arms_nested_reset( ArmsNested * value ) +{ + value->lead = 0; + value->outer = 0; + value->inner = 0; + value->x = 0; + value->y = 0; + value->z = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arms_nested_reset_raw_( void * storage ) { arms_nested_reset( (ArmsNested *) storage ); } + +extern const TableTypeInfo schema_example_arms_nested_info_; +static SCHEMA_UNUSED const TableTypeInfo * arms_nested_table_type(void) { return &schema_example_arms_nested_info_; } +static SCHEMA_UNUSED void arr_eleven_reset( ArrEleven * value ) +{ + eleven_reset( &value->items[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 9 ); i++ ) { value->items[i] = value->items[0]; } } +} + +static SCHEMA_UNUSED void schema_example_arr_eleven_reset_raw_( void * storage ) { arr_eleven_reset( (ArrEleven *) storage ); } + +extern const TableTypeInfo schema_example_arr_eleven_info_; +static SCHEMA_UNUSED const TableTypeInfo * arr_eleven_table_type(void) { return &schema_example_arr_eleven_info_; } +static SCHEMA_UNUSED void arr_nested_reset( ArrNested * value ) +{ + value->lead = 0; + eleven_reset( &value->items[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 4 ); i++ ) { value->items[i] = value->items[0]; } } + value->items_count = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arr_nested_reset_raw_( void * storage ) { arr_nested_reset( (ArrNested *) storage ); } + +extern const TableTypeInfo schema_example_arr_nested_info_; +static SCHEMA_UNUSED const TableTypeInfo * arr_nested_table_type(void) { return &schema_example_arr_nested_info_; } +static SCHEMA_UNUSED void arr_tri3_reset( ArrTri3 * value ) +{ + tri3_reset( &value->items[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 10 ); i++ ) { value->items[i] = value->items[0]; } } + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_arr_tri3_reset_raw_( void * storage ) { arr_tri3_reset( (ArrTri3 *) storage ); } + +extern const TableTypeInfo schema_example_arr_tri3_info_; +static SCHEMA_UNUSED const TableTypeInfo * arr_tri3_table_type(void) { return &schema_example_arr_tri3_info_; } +static SCHEMA_UNUSED void arr_uneven_reset( ArrUneven * value ) +{ + value->lead = 0; + memset( &value->items[0], 0, sizeof( value->items[0] ) ); + { int32_t i; for ( i = 1; i < (int32_t) ( 3 ); i++ ) { value->items[i] = value->items[0]; } } + value->items_count = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arr_uneven_reset_raw_( void * storage ) { arr_uneven_reset( (ArrUneven *) storage ); } + +extern const TableTypeInfo schema_example_arr_uneven_info_; +static SCHEMA_UNUSED const TableTypeInfo * arr_uneven_table_type(void) { return &schema_example_arr_uneven_info_; } +static SCHEMA_UNUSED void block_reset( Block * value ) +{ + memset( value->data, 0, sizeof( value->data ) ); + value->data_length = 0; +} + +static SCHEMA_UNUSED void schema_example_block_reset_raw_( void * storage ) { block_reset( (Block *) storage ); } + +extern const TableTypeInfo schema_example_block_info_; +static SCHEMA_UNUSED const TableTypeInfo * block_table_type(void) { return &schema_example_block_info_; } +static SCHEMA_UNUSED void chat_reset( Chat * value ) +{ + memset( value->text, 0, sizeof( value->text ) ); + value->text_length = 0; +} + +static SCHEMA_UNUSED void schema_example_chat_reset_raw_( void * storage ) { chat_reset( (Chat *) storage ); } + +extern const TableTypeInfo schema_example_chat_info_; +static SCHEMA_UNUSED const TableTypeInfo * chat_table_type(void) { return &schema_example_chat_info_; } +static SCHEMA_UNUSED void compressed_probe_reset( CompressedProbe * value ) +{ + value->boundary = 0.0f; + value->offset = 0.0f; +} + +static SCHEMA_UNUSED void schema_example_compressed_probe_reset_raw_( void * storage ) { compressed_probe_reset( (CompressedProbe *) storage ); } + +extern const TableTypeInfo schema_example_compressed_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * compressed_probe_table_type(void) { return &schema_example_compressed_probe_info_; } +static SCHEMA_UNUSED void default_arm_reset( DefaultArm * value ) +{ + probe_config_reset( &value->entries[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 2 ); i++ ) { value->entries[i] = value->entries[0]; } } + value->entries_count = 0; + value->marker = 0; +} + +static SCHEMA_UNUSED void schema_example_default_arm_reset_raw_( void * storage ) { default_arm_reset( (DefaultArm *) storage ); } + +extern const TableTypeInfo schema_example_default_arm_info_; +static SCHEMA_UNUSED const TableTypeInfo * default_arm_table_type(void) { return &schema_example_default_arm_info_; } +static SCHEMA_UNUSED void default_bulk_arm_reset( DefaultBulkArm * value ) +{ + default_arm_reset( &value->payload ); + memset( value->data, 0, sizeof( value->data ) ); + value->data_length = 0; +} + +static SCHEMA_UNUSED void schema_example_default_bulk_arm_reset_raw_( void * storage ) { default_bulk_arm_reset( (DefaultBulkArm *) storage ); } + +extern const TableTypeInfo schema_example_default_bulk_arm_info_; +static SCHEMA_UNUSED const TableTypeInfo * default_bulk_arm_table_type(void) { return &schema_example_default_bulk_arm_info_; } +static SCHEMA_UNUSED void default_choice_packet_reset( DefaultChoicePacket * value ) +{ + memset( &value->choice, 0, sizeof( value->choice ) ); +} + +static SCHEMA_UNUSED void schema_example_default_choice_packet_reset_raw_( void * storage ) { default_choice_packet_reset( (DefaultChoicePacket *) storage ); } + +extern const TableTypeInfo schema_example_default_choice_packet_info_; +static SCHEMA_UNUSED const TableTypeInfo * default_choice_packet_table_type(void) { return &schema_example_default_choice_packet_info_; } +static SCHEMA_UNUSED void eleven_reset( Eleven * value ) +{ + value->a = 0; + value->b = 0; +} + +static SCHEMA_UNUSED void schema_example_eleven_reset_raw_( void * storage ) { eleven_reset( (Eleven *) storage ); } + +extern const TableTypeInfo schema_example_eleven_info_; +static SCHEMA_UNUSED const TableTypeInfo * eleven_table_type(void) { return &schema_example_eleven_info_; } +static SCHEMA_UNUSED void empty_a_reset( EmptyA * value ) +{ + memset( value, 0, sizeof( *value ) ); +} + +static SCHEMA_UNUSED void schema_example_empty_a_reset_raw_( void * storage ) { empty_a_reset( (EmptyA *) storage ); } + +extern const TableTypeInfo schema_example_empty_a_info_; +static SCHEMA_UNUSED const TableTypeInfo * empty_a_table_type(void) { return &schema_example_empty_a_info_; } +static SCHEMA_UNUSED void empty_b_reset( EmptyB * value ) +{ + memset( value, 0, sizeof( *value ) ); +} + +static SCHEMA_UNUSED void schema_example_empty_b_reset_raw_( void * storage ) { empty_b_reset( (EmptyB *) storage ); } + +extern const TableTypeInfo schema_example_empty_b_info_; +static SCHEMA_UNUSED const TableTypeInfo * empty_b_table_type(void) { return &schema_example_empty_b_info_; } +static SCHEMA_UNUSED void expression_probe_reset( ExpressionProbe * value ) +{ + value->hardpoint_index = 0; + value->spin_rate = 1024; +} + +static SCHEMA_UNUSED void schema_example_expression_probe_reset_raw_( void * storage ) { expression_probe_reset( (ExpressionProbe *) storage ); } + +extern const TableTypeInfo schema_example_expression_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * expression_probe_table_type(void) { return &schema_example_expression_probe_info_; } +static SCHEMA_UNUSED void extreme_probe_reset( ExtremeProbe * value ) +{ + value->floor_bound = 0; + value->doubled_floor = 0; + value->ceiling_range = 1ull; + value->floor_default = ( -9223372036854775807ll - 1 ); + value->ceiling_default = 18446744073709551615ull; +} + +static SCHEMA_UNUSED void schema_example_extreme_probe_reset_raw_( void * storage ) { extreme_probe_reset( (ExtremeProbe *) storage ); } + +extern const TableTypeInfo schema_example_extreme_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * extreme_probe_table_type(void) { return &schema_example_extreme_probe_info_; } +static SCHEMA_UNUSED void extreme_row_reset( ExtremeRow * value ) +{ + value->clamped_floor = 0; + value->clamped_ceiling = 1ull; + value->floor_def = ( -9223372036854775807ll - 1 ); + value->ceiling_def = 18446744073709551615ull; +} + +static SCHEMA_UNUSED void schema_example_extreme_row_reset_raw_( void * storage ) { extreme_row_reset( (ExtremeRow *) storage ); } + +extern const TableTypeInfo schema_example_extreme_row_info_; +static SCHEMA_UNUSED const TableTypeInfo * extreme_row_table_type(void) { return &schema_example_extreme_row_info_; } +static SCHEMA_UNUSED void f13_reset( F13 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); +} + +static SCHEMA_UNUSED void schema_example_f13_reset_raw_( void * storage ) { f13_reset( (F13 *) storage ); } + +extern const TableTypeInfo schema_example_f13_info_; +static SCHEMA_UNUSED const TableTypeInfo * f13_table_type(void) { return &schema_example_f13_info_; } +static SCHEMA_UNUSED void handle_reset( Handle * value ) +{ + value->object_id = 0; + value->object_sequence = 0; +} + +static SCHEMA_UNUSED void schema_example_handle_reset_raw_( void * storage ) { handle_reset( (Handle *) storage ); } + +extern const TableTypeInfo schema_example_handle_info_; +static SCHEMA_UNUSED const TableTypeInfo * handle_table_type(void) { return &schema_example_handle_info_; } +static SCHEMA_UNUSED void heartbeat_reset( Heartbeat * value ) +{ + memset( value, 0, sizeof( *value ) ); +} + +static SCHEMA_UNUSED void schema_example_heartbeat_reset_raw_( void * storage ) { heartbeat_reset( (Heartbeat *) storage ); } + +extern const TableTypeInfo schema_example_heartbeat_info_; +static SCHEMA_UNUSED const TableTypeInfo * heartbeat_table_type(void) { return &schema_example_heartbeat_info_; } +static SCHEMA_UNUSED void holds_empty_union_reset( HoldsEmptyUnion * value ) +{ + value->lead = 0; + memset( &value->u, 0, sizeof( value->u ) ); + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_holds_empty_union_reset_raw_( void * storage ) { holds_empty_union_reset( (HoldsEmptyUnion *) storage ); } + +extern const TableTypeInfo schema_example_holds_empty_union_info_; +static SCHEMA_UNUSED const TableTypeInfo * holds_empty_union_table_type(void) { return &schema_example_holds_empty_union_info_; } +static SCHEMA_UNUSED void holds_uneven_reset( HoldsUneven * value ) +{ + value->lead = 0; + memset( &value->u, 0, sizeof( value->u ) ); + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_holds_uneven_reset_raw_( void * storage ) { holds_uneven_reset( (HoldsUneven *) storage ); } + +extern const TableTypeInfo schema_example_holds_uneven_info_; +static SCHEMA_UNUSED const TableTypeInfo * holds_uneven_table_type(void) { return &schema_example_holds_uneven_info_; } +static SCHEMA_UNUSED void input_reset( Input * value ) +{ + value->stick_x = 0.0f; + value->stick_y = 0.0f; + value->throttle = 0.0f; + value->yaw = 0.0f; + value->pitch = 0.0f; + value->fire = 0; + value->alt_fire = 0; + value->boost = 0; + value->brake = 0; + value->aim = 0; + value->lock_on = 0; + value->zoom = 0; + value->ping = 0; +} + +static SCHEMA_UNUSED void schema_example_input_reset_raw_( void * storage ) { input_reset( (Input *) storage ); } + +extern const TableTypeInfo schema_example_input_info_; +static SCHEMA_UNUSED const TableTypeInfo * input_table_type(void) { return &schema_example_input_info_; } +static SCHEMA_UNUSED void input_packet_reset( InputPacket * value ) +{ + value->synchronize_sequence = 0; + value->current_frame = 0; + value->start_frame = 0; + input_reset( &value->inputs[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 16 ); i++ ) { value->inputs[i] = value->inputs[0]; } } + value->inputs_count = 0; +} + +static SCHEMA_UNUSED void schema_example_input_packet_reset_raw_( void * storage ) { input_packet_reset( (InputPacket *) storage ); } + +extern const TableTypeInfo schema_example_input_packet_info_; +static SCHEMA_UNUSED const TableTypeInfo * input_packet_table_type(void) { return &schema_example_input_packet_info_; } +static SCHEMA_UNUSED void narrow_reset( Narrow * value ) +{ + value->n = 0; +} + +static SCHEMA_UNUSED void schema_example_narrow_reset_raw_( void * storage ) { narrow_reset( (Narrow *) storage ); } + +extern const TableTypeInfo schema_example_narrow_info_; +static SCHEMA_UNUSED const TableTypeInfo * narrow_table_type(void) { return &schema_example_narrow_info_; } +static SCHEMA_UNUSED void probe_array_reset( ProbeArray * value ) +{ + probe_sample_reset( &value->samples[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 2 ); i++ ) { value->samples[i] = value->samples[0]; } } + probe_config_reset( &value->config ); +} + +static SCHEMA_UNUSED void schema_example_probe_array_reset_raw_( void * storage ) { probe_array_reset( (ProbeArray *) storage ); } + +extern const TableTypeInfo schema_example_probe_array_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_array_table_type(void) { return &schema_example_probe_array_info_; } +static SCHEMA_UNUSED void probe_bits_reset( ProbeBits * value ) +{ + value->small = 0; + value->boundary = 0; + value->wide = 0; + value->sensor = 0; + value->nonce = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_bits_reset_raw_( void * storage ) { probe_bits_reset( (ProbeBits *) storage ); } + +extern const TableTypeInfo schema_example_probe_bits_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_bits_table_type(void) { return &schema_example_probe_bits_info_; } +static SCHEMA_UNUSED void probe_collider_reset( ProbeCollider * value ) +{ + value->armor = 0; + memset( &value->shape, 0, sizeof( value->shape ) ); + memset( &value->backup, 0, sizeof( value->backup ) ); + memset( &value->extras[0], 0, sizeof( value->extras[0] ) ); + { int32_t i; for ( i = 1; i < (int32_t) ( 2 ); i++ ) { value->extras[i] = value->extras[0]; } } + value->extras_count = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_collider_reset_raw_( void * storage ) { probe_collider_reset( (ProbeCollider *) storage ); } + +extern const TableTypeInfo schema_example_probe_collider_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_collider_table_type(void) { return &schema_example_probe_collider_info_; } +static SCHEMA_UNUSED void probe_config_reset( ProbeConfig * value ) +{ + value->retries = -1; + value->preferred = WEAPON_RAILGUN; +} + +static SCHEMA_UNUSED void schema_example_probe_config_reset_raw_( void * storage ) { probe_config_reset( (ProbeConfig *) storage ); } + +extern const TableTypeInfo schema_example_probe_config_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_config_table_type(void) { return &schema_example_probe_config_info_; } +static SCHEMA_UNUSED void probe_header_reset( ProbeHeader * value ) +{ + value->version = 0; + value->probe_id = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_header_reset_raw_( void * storage ) { probe_header_reset( (ProbeHeader *) storage ); } + +extern const TableTypeInfo schema_example_probe_header_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_header_table_type(void) { return &schema_example_probe_header_info_; } +static SCHEMA_UNUSED void probe_report_reset( ProbeReport * value ) +{ + probe_header_reset( &value->header ); + value->flags = 0; + test_reset( &value->echo ); +} + +static SCHEMA_UNUSED void schema_example_probe_report_reset_raw_( void * storage ) { probe_report_reset( (ProbeReport *) storage ); } + +extern const TableTypeInfo schema_example_probe_report_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_report_table_type(void) { return &schema_example_probe_report_info_; } +static SCHEMA_UNUSED void probe_ring_reset( ProbeRing * value ) +{ + value->radius = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_ring_reset_raw_( void * storage ) { probe_ring_reset( (ProbeRing *) storage ); } + +extern const TableTypeInfo schema_example_probe_ring_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_ring_table_type(void) { return &schema_example_probe_ring_info_; } +static SCHEMA_UNUSED void probe_sample_reset( ProbeSample * value ) +{ + value->active = 1; + value->orientation = 0.0f; + value->raw_delta = 0; + value->big_delta = 0; + value->weapon = WEAPON_NONE; + value->has_target = 0; + value->target_id = 0; + value->idle_ticks = 0; + memset( value->samples, 0, sizeof( value->samples ) ); + value->samples_count = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_sample_reset_raw_( void * storage ) { probe_sample_reset( (ProbeSample *) storage ); } + +extern const TableTypeInfo schema_example_probe_sample_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_sample_table_type(void) { return &schema_example_probe_sample_info_; } +static SCHEMA_UNUSED void probe_slab_reset( ProbeSlab * value ) +{ + value->width = 0; + value->height = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_slab_reset_raw_( void * storage ) { probe_slab_reset( (ProbeSlab *) storage ); } + +extern const TableTypeInfo schema_example_probe_slab_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_slab_table_type(void) { return &schema_example_probe_slab_info_; } +static SCHEMA_UNUSED void quantized_position_reset( QuantizedPosition * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; +} + +static SCHEMA_UNUSED void schema_example_quantized_position_reset_raw_( void * storage ) { quantized_position_reset( (QuantizedPosition *) storage ); } + +extern const TableTypeInfo schema_example_quantized_position_info_; +static SCHEMA_UNUSED const TableTypeInfo * quantized_position_table_type(void) { return &schema_example_quantized_position_info_; } +static SCHEMA_UNUSED void quantized_rotation_reset( QuantizedRotation * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; + value->w = 0; +} + +static SCHEMA_UNUSED void schema_example_quantized_rotation_reset_raw_( void * storage ) { quantized_rotation_reset( (QuantizedRotation *) storage ); } + +extern const TableTypeInfo schema_example_quantized_rotation_info_; +static SCHEMA_UNUSED const TableTypeInfo * quantized_rotation_table_type(void) { return &schema_example_quantized_rotation_info_; } +static SCHEMA_UNUSED void quantized_velocity_reset( QuantizedVelocity * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; +} + +static SCHEMA_UNUSED void schema_example_quantized_velocity_reset_raw_( void * storage ) { quantized_velocity_reset( (QuantizedVelocity *) storage ); } + +extern const TableTypeInfo schema_example_quantized_velocity_info_; +static SCHEMA_UNUSED const TableTypeInfo * quantized_velocity_table_type(void) { return &schema_example_quantized_velocity_info_; } +static SCHEMA_UNUSED void quat_reset( Quat * value ) +{ + value->x = 0.0; + value->y = 0.0; + value->z = 0.0; + value->w = 0.0; +} + +static SCHEMA_UNUSED void schema_example_quat_reset_raw_( void * storage ) { quat_reset( (Quat *) storage ); } + +extern const TableTypeInfo schema_example_quat_info_; +static SCHEMA_UNUSED const TableTypeInfo * quat_table_type(void) { return &schema_example_quat_info_; } +static SCHEMA_UNUSED void regain_after_align_reset( RegainAfterAlign * value ) +{ + value->lead = 0; + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; + memset( value->s, 0, sizeof( value->s ) ); + value->s_length = 0; + value->p = 0; + value->q = 0; + value->r = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_regain_after_align_reset_raw_( void * storage ) { regain_after_align_reset( (RegainAfterAlign *) storage ); } + +extern const TableTypeInfo schema_example_regain_after_align_info_; +static SCHEMA_UNUSED const TableTypeInfo * regain_after_align_table_type(void) { return &schema_example_regain_after_align_info_; } +static SCHEMA_UNUSED void render_block_reset( RenderBlock * value ) +{ + value->worker_index = 0; + value->sprite_count_hint = 0; + render_sprite_reset( &value->sprites[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 64 ); i++ ) { value->sprites[i] = value->sprites[0]; } } + value->sprites_count = 0; +} + +static SCHEMA_UNUSED void schema_example_render_block_reset_raw_( void * storage ) { render_block_reset( (RenderBlock *) storage ); } + +extern const TableTypeInfo schema_example_render_block_info_; +static SCHEMA_UNUSED const TableTypeInfo * render_block_table_type(void) { return &schema_example_render_block_info_; } +static SCHEMA_UNUSED void render_sprite_reset( RenderSprite * value ) +{ + value->sort_key = 0; + value->mesh_id = 0; + value->material_id = 0; + value->layer = 0; + value->team = TEAM_NONE; +} + +static SCHEMA_UNUSED void schema_example_render_sprite_reset_raw_( void * storage ) { render_sprite_reset( (RenderSprite *) storage ); } + +extern const TableTypeInfo schema_example_render_sprite_info_; +static SCHEMA_UNUSED const TableTypeInfo * render_sprite_table_type(void) { return &schema_example_render_sprite_info_; } +static SCHEMA_UNUSED void rigid_body_reset( RigidBody * value ) +{ + vec3_reset( &value->position ); + quat_reset( &value->orientation ); + value->at_rest = 0; + vec3_reset( &value->linear_velocity ); + vec3_reset( &value->angular_velocity ); +} + +static SCHEMA_UNUSED void schema_example_rigid_body_reset_raw_( void * storage ) { rigid_body_reset( (RigidBody *) storage ); } + +extern const TableTypeInfo schema_example_rigid_body_info_; +static SCHEMA_UNUSED const TableTypeInfo * rigid_body_table_type(void) { return &schema_example_rigid_body_info_; } +static SCHEMA_UNUSED void ship_create_reset( ShipCreate * value ) +{ + value->ship_type = SHIP_TYPE_NONE; + quantized_position_reset( &value->position ); + quantized_rotation_reset( &value->rotation ); + quantized_velocity_reset( &value->linear_velocity ); + value->has_flags = 0; + value->flags = 0; + value->team = TEAM_NONE; + value->health = 0; + value->thrust = 0; + value->pending = PENDING_NONE; +} + +static SCHEMA_UNUSED void schema_example_ship_create_reset_raw_( void * storage ) { ship_create_reset( (ShipCreate *) storage ); } + +extern const TableTypeInfo schema_example_ship_create_info_; +static SCHEMA_UNUSED const TableTypeInfo * ship_create_table_type(void) { return &schema_example_ship_create_info_; } +static SCHEMA_UNUSED void sole_reset( Sole * value ) +{ + value->only = 0; +} + +static SCHEMA_UNUSED void schema_example_sole_reset_raw_( void * storage ) { sole_reset( (Sole *) storage ); } + +extern const TableTypeInfo schema_example_sole_info_; +static SCHEMA_UNUSED const TableTypeInfo * sole_table_type(void) { return &schema_example_sole_info_; } +static SCHEMA_UNUSED void span_chunk_reset( SpanChunk * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_chunk_reset_raw_( void * storage ) { span_chunk_reset( (SpanChunk *) storage ); } + +extern const TableTypeInfo schema_example_span_chunk_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_chunk_table_type(void) { return &schema_example_span_chunk_info_; } +static SCHEMA_UNUSED void span_f64_reset( SpanF64 * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_f64_reset_raw_( void * storage ) { span_f64_reset( (SpanF64 *) storage ); } + +extern const TableTypeInfo schema_example_span_f64_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_f64_table_type(void) { return &schema_example_span_f64_info_; } +static SCHEMA_UNUSED void span_i64_reset( SpanI64 * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_i64_reset_raw_( void * storage ) { span_i64_reset( (SpanI64 *) storage ); } + +extern const TableTypeInfo schema_example_span_i64_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_i64_table_type(void) { return &schema_example_span_i64_info_; } +static SCHEMA_UNUSED void span_one_reset( SpanOne * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_one_reset_raw_( void * storage ) { span_one_reset( (SpanOne *) storage ); } + +extern const TableTypeInfo schema_example_span_one_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_one_table_type(void) { return &schema_example_span_one_info_; } +static SCHEMA_UNUSED void span_tail_reset( SpanTail * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_span_tail_reset_raw_( void * storage ) { span_tail_reset( (SpanTail *) storage ); } + +extern const TableTypeInfo schema_example_span_tail_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_tail_table_type(void) { return &schema_example_span_tail_info_; } +static SCHEMA_UNUSED void span_twice_reset( SpanTwice * value ) +{ + memset( value->a, 0, sizeof( value->a ) ); + memset( value->b, 0, sizeof( value->b ) ); +} + +static SCHEMA_UNUSED void schema_example_span_twice_reset_raw_( void * storage ) { span_twice_reset( (SpanTwice *) storage ); } + +extern const TableTypeInfo schema_example_span_twice_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_twice_table_type(void) { return &schema_example_span_twice_info_; } +static SCHEMA_UNUSED void span_u64_reset( SpanU64 * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_u64_reset_raw_( void * storage ) { span_u64_reset( (SpanU64 *) storage ); } + +extern const TableTypeInfo schema_example_span_u64_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_u64_table_type(void) { return &schema_example_span_u64_info_; } +static SCHEMA_UNUSED void strs_reset( Strs * value ) +{ + value->lead = 0; + memset( value->s, 0, sizeof( value->s ) ); + value->s_length = 0; + memset( value->b, 0, sizeof( value->b ) ); + value->b_length = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_strs_reset_raw_( void * storage ) { strs_reset( (Strs *) storage ); } + +extern const TableTypeInfo schema_example_strs_info_; +static SCHEMA_UNUSED const TableTypeInfo * strs_table_type(void) { return &schema_example_strs_info_; } +static SCHEMA_UNUSED void test_reset( Test * value ) +{ + value->test_a = 0; + value->test_b = 0; + value->test_c = 0; + value->test_d = 0; +} + +static SCHEMA_UNUSED void schema_example_test_reset_raw_( void * storage ) { test_reset( (Test *) storage ); } + +extern const TableTypeInfo schema_example_test_info_; +static SCHEMA_UNUSED const TableTypeInfo * test_table_type(void) { return &schema_example_test_info_; } +static SCHEMA_UNUSED void test_data_reset( TestData * value ) +{ + value->a = 0; + value->b = 0; + value->c = 0; + value->d = 0; + value->e = 0; + value->f = 0; + value->g = 0; + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; + value->float_value = 0.0f; + value->compressed_float_value = 0.0f; + value->double_value = 0.0; + value->int8_value = 0; + value->int16_value = 0; + value->uint8_value = 0; + value->uint16_value = 0; + value->uint32_value = 0; + value->uint64_value = 0; + value->int64_full = 0; + value->int64_range = 0; + memset( value->fixed_bytes, 0, sizeof( value->fixed_bytes ) ); + memset( value->text, 0, sizeof( value->text ) ); + value->text_length = 0; +} + +static SCHEMA_UNUSED void schema_example_test_data_reset_raw_( void * storage ) { test_data_reset( (TestData *) storage ); } + +extern const TableTypeInfo schema_example_test_data_info_; +static SCHEMA_UNUSED const TableTypeInfo * test_data_table_type(void) { return &schema_example_test_data_info_; } +static SCHEMA_UNUSED void tri3_reset( Tri3 * value ) +{ + value->a = 0; + value->b = 0; +} + +static SCHEMA_UNUSED void schema_example_tri3_reset_raw_( void * storage ) { tri3_reset( (Tri3 *) storage ); } + +extern const TableTypeInfo schema_example_tri3_info_; +static SCHEMA_UNUSED const TableTypeInfo * tri3_table_type(void) { return &schema_example_tri3_info_; } +static SCHEMA_UNUSED void trio_reset( Trio * value ) +{ + value->a = 0; + value->b = 0; + value->c = 0; +} + +static SCHEMA_UNUSED void schema_example_trio_reset_raw_( void * storage ) { trio_reset( (Trio *) storage ); } + +extern const TableTypeInfo schema_example_trio_info_; +static SCHEMA_UNUSED const TableTypeInfo * trio_table_type(void) { return &schema_example_trio_info_; } +static SCHEMA_UNUSED void trio_first_reset( TrioFirst * value ) +{ + trio_reset( &value->inner ); + value->trailer = 0; +} + +static SCHEMA_UNUSED void schema_example_trio_first_reset_raw_( void * storage ) { trio_first_reset( (TrioFirst *) storage ); } + +extern const TableTypeInfo schema_example_trio_first_info_; +static SCHEMA_UNUSED const TableTypeInfo * trio_first_table_type(void) { return &schema_example_trio_first_info_; } +static SCHEMA_UNUSED void trio_sole_reset( TrioSole * value ) +{ + trio_reset( &value->inner ); +} + +static SCHEMA_UNUSED void schema_example_trio_sole_reset_raw_( void * storage ) { trio_sole_reset( (TrioSole *) storage ); } + +extern const TableTypeInfo schema_example_trio_sole_info_; +static SCHEMA_UNUSED const TableTypeInfo * trio_sole_table_type(void) { return &schema_example_trio_sole_info_; } +static SCHEMA_UNUSED void trio_straddle_reset( TrioStraddle * value ) +{ + value->pad0 = 0; + value->pad1 = 0; + value->pad2 = 0; + value->pad3 = 0; + value->pad4 = 0; + value->pad5 = 0; + trio_reset( &value->inner ); +} + +static SCHEMA_UNUSED void schema_example_trio_straddle_reset_raw_( void * storage ) { trio_straddle_reset( (TrioStraddle *) storage ); } + +extern const TableTypeInfo schema_example_trio_straddle_info_; +static SCHEMA_UNUSED const TableTypeInfo * trio_straddle_table_type(void) { return &schema_example_trio_straddle_info_; } +static SCHEMA_UNUSED void vec2_reset( Vec2 * value ) +{ + value->x = 0.0; + value->y = 0.0; +} + +static SCHEMA_UNUSED void schema_example_vec2_reset_raw_( void * storage ) { vec2_reset( (Vec2 *) storage ); } + +extern const TableTypeInfo schema_example_vec2_info_; +static SCHEMA_UNUSED const TableTypeInfo * vec2_table_type(void) { return &schema_example_vec2_info_; } +static SCHEMA_UNUSED void vec3_reset( Vec3 * value ) +{ + value->x = 0.0; + value->y = 0.0; + value->z = 0.0; +} + +static SCHEMA_UNUSED void schema_example_vec3_reset_raw_( void * storage ) { vec3_reset( (Vec3 *) storage ); } + +extern const TableTypeInfo schema_example_vec3_info_; +static SCHEMA_UNUSED const TableTypeInfo * vec3_table_type(void) { return &schema_example_vec3_info_; } +static SCHEMA_UNUSED void w1_reset( W1 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w1_reset_raw_( void * storage ) { w1_reset( (W1 *) storage ); } + +extern const TableTypeInfo schema_example_w1_info_; +static SCHEMA_UNUSED const TableTypeInfo * w1_table_type(void) { return &schema_example_w1_info_; } +static SCHEMA_UNUSED void w13_reset( W13 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w13_reset_raw_( void * storage ) { w13_reset( (W13 *) storage ); } + +extern const TableTypeInfo schema_example_w13_info_; +static SCHEMA_UNUSED const TableTypeInfo * w13_table_type(void) { return &schema_example_w13_info_; } +static SCHEMA_UNUSED void w17_reset( W17 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w17_reset_raw_( void * storage ) { w17_reset( (W17 *) storage ); } + +extern const TableTypeInfo schema_example_w17_info_; +static SCHEMA_UNUSED const TableTypeInfo * w17_table_type(void) { return &schema_example_w17_info_; } +static SCHEMA_UNUSED void w26_reset( W26 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w26_reset_raw_( void * storage ) { w26_reset( (W26 *) storage ); } + +extern const TableTypeInfo schema_example_w26_info_; +static SCHEMA_UNUSED const TableTypeInfo * w26_table_type(void) { return &schema_example_w26_info_; } +static SCHEMA_UNUSED void w50_reset( W50 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w50_reset_raw_( void * storage ) { w50_reset( (W50 *) storage ); } + +extern const TableTypeInfo schema_example_w50_info_; +static SCHEMA_UNUSED const TableTypeInfo * w50_table_type(void) { return &schema_example_w50_info_; } +static SCHEMA_UNUSED void w52_reset( W52 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w52_reset_raw_( void * storage ) { w52_reset( (W52 *) storage ); } + +extern const TableTypeInfo schema_example_w52_info_; +static SCHEMA_UNUSED const TableTypeInfo * w52_table_type(void) { return &schema_example_w52_info_; } +static SCHEMA_UNUSED void wide_reset( Wide * value ) +{ + value->w = 0; +} + +static SCHEMA_UNUSED void schema_example_wide_reset_raw_( void * storage ) { wide_reset( (Wide *) storage ); } + +extern const TableTypeInfo schema_example_wide_info_; +static SCHEMA_UNUSED const TableTypeInfo * wide_table_type(void) { return &schema_example_wide_info_; } +const UnitViewInfo * schema_example_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_example_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/internal/check/check.go b/internal/check/check.go index 8528364f0..ab193b565 100644 --- a/internal/check/check.go +++ b/internal/check/check.go @@ -3468,7 +3468,7 @@ func (c *checker) checkClaimedNames() { // unit rather than to a declaration, so they are claimed once at unit // scope, as ProtocolId is (SPEC §6.1), and a unit that grows its first // table must not find a name that was free yesterday taken today. - for _, gen := range []string{"UnitView", "UnitViewInfo", "ViewType", "ViewVocabulary", "ViewVariant", "ViewConstant"} { + for _, gen := range []string{"UnitView", "unit_view", "UnitViewInfo", "ViewType", "ViewVocabulary", "ViewVariant", "ViewConstant"} { add(gen, "the generated unit registry (docs/SPEC-TABLES.md §8.3)", unitPos) } // names the generated Rust references unqualified: the serialize imports @@ -3741,6 +3741,9 @@ func (c *checker) checkClaimedNames() { accessor := name + ir.GoExportName(f.Name) add(accessor, why, d.DeclPos()) add(accessor+"Span", why, d.DeclPos()) + add(accessor+"Rows", why, d.DeclPos()) + add(accessor+"RowsConst", why, d.DeclPos()) + add(accessor+"SpanConst", why, d.DeclPos()) } // AND A MAP claims its whole lookup surface on the table // that declares it:
followed by Entry and @@ -3757,10 +3760,11 @@ func (c *checker) checkClaimedNames() { for _, verb := range ir.MapFieldVerbs { add(base+verb, whyMap, d.DeclPos()) } + add(base+"FindMut", whyMap, d.DeclPos()) } - // AND AN UNBOUNDED ARRAY claims three names on the table - // that declares it:
followed by Add, Each and - // Erase (docs/SPEC-TABLES.md §2.9, §11). Three where a map + // AND AN UNBOUNDED ARRAY claims four names on the table + // that declares it:
followed by Add, Each, + // Erase and At (docs/SPEC-TABLES.md §2.9, §11). Four where a map // claims eight, and the difference is the key on both sides: // an append needs none, so there is no entry to name, no // insert, no find and no index to accelerate. @@ -3773,6 +3777,7 @@ func (c *checker) checkClaimedNames() { for _, verb := range ir.ListFieldVerbs { add(base+verb, whyList, d.DeclPos()) } + add(base+"At", whyList, d.DeclPos()) } } } @@ -3861,7 +3866,12 @@ var tableGeneratedVerbs = []string{ // members, so each is a free function under its owner's name, and the // comment above this list is the rule they are added under: a port that // spells the surface otherwise adds its spellings here. - "BuilderInit", "BuilderShutdown", "BuilderLock", "BuilderRoot", + "BuilderInit", "BuilderInitWithAllocator", "BuilderShutdown", "BuilderLock", "BuilderRoot", + "OpenEx", "BlockOpenEx", "BlockOpenConst", "BlockOpenConstEx", "BlockOpenCheck", "BlockConst", "BlockBytesConst", + "CookWithAllocator", "CookMeasureWithAllocator", "CookExtent", + "MeasureRetainWithAllocator", "SaveRetainWithAllocator", + "LoadMeasureMessages", "LoadMeasureMessagesEx", "MeasureMessagesWithAllocator", "SaveMessagesWithAllocator", + "LoadMeasureEx", "MeasureWithAllocator", "SaveWithAllocator", "ToJsonWithAllocator", "ToJsonMeasureWithAllocator", "BlockStorageCreate", "BlockStorageDestroy", "BlockType", // the C# BLITTABLE records take claimed suffixes in the package namespace // rather than a nested namespace of their own: a generated namespace named @@ -4076,7 +4086,7 @@ func (c *checker) cReservedMacros() map[string]bool { "SCHEMA_UNUSED", "SCHEMA_C_READ_INLINE", "SCHEMA_C_WRITE_INLINE", "SCHEMA_C_SPINE_INLINE_DEFINED", "SCHEMA_UTF8_VALID_DEFINED", "SCHEMA_FLAG_APPEND_DEFINED", // the table backend's (internal/codegen/ctable) - "SCHEMA_TABLE_ALIGNOF", "SCHEMA_TABLE_ATOMIC", "SCHEMA_TABLE_STATIC_ASSERT", + "SCHEMA_C_ALIGN16", "SCHEMA_TABLE_ALIGNOF", "SCHEMA_TABLE_ATOMIC", "SCHEMA_TABLE_STATIC_ASSERT", "SCHEMA_TABLE_KEYED_AT", } { out[fixed] = true diff --git a/internal/check/tables_test.go b/internal/check/tables_test.go index 2cd52aaa9..d3ef1c228 100644 --- a/internal/check/tables_test.go +++ b/internal/check/tables_test.go @@ -409,6 +409,8 @@ func TestTableRefusals(t *testing.T) { // refusals the view brought and nothing held {name: "a declaration colliding with a unit-scope registry name", want: "generated unit registry", src: "package t\ntype ViewVariant { x int32 }\n"}, + {name: "the C unit registry function in a table-free unit", want: "generated unit registry", + src: "package t\ntype unit_view { x int32 }\n"}, {name: "a declaration colliding with an out-of-closure type's view descriptor", want: "generated view descriptor", src: "package t\ntable Tab { x int32 }\ntype Orphan { y int32 }\ntype OrphanTableFields { z int32 }\n"}, {name: "a schema file named for the unit's view file", want: "the same name as the unit's view file", diff --git a/internal/codegen/c/c.go b/internal/codegen/c/c.go index fc69d4c9a..36182ee0f 100644 --- a/internal/codegen/c/c.go +++ b/internal/codegen/c/c.go @@ -40,6 +40,7 @@ import ( "strconv" "strings" + "github.com/mas-bandwidth/schema/v2/internal/codegen/ccommon" "github.com/mas-bandwidth/schema/v2/ir" ) @@ -151,6 +152,9 @@ func (g *gen) assembleHeader() []byte { if strings.Contains(body, "floor") { h.WriteString("#include /* floor — the quantize pair */\n") } + if strings.Contains(body, "SCHEMA_C_ALIGN16") { + h.WriteString(ccommon.Align16) + } if g.needs128 { // serialize_int128_t / serialize_uint128_t are STORAGE here, so the // runtime header has to reach the data header and not only the wire one @@ -401,6 +405,9 @@ func (g *gen) emitStruct(d *ir.Struct) { g.pf("typedef struct %s {\n", d.Name) for _, f := range d.Fields { g.pf("%s", ir.DocComment(f.Doc, " ", "//")) + if f.Type.Width == 128 && ir.TableClosure(g.unit)[d.Name] { + g.pf(" SCHEMA_C_ALIGN16 ") + } g.emitField(f) } g.pf("} %s;\n\n", d.Name) diff --git a/internal/codegen/ccommon/align.go b/internal/codegen/ccommon/align.go new file mode 100644 index 000000000..16cdb8e35 --- /dev/null +++ b/internal/codegen/ccommon/align.go @@ -0,0 +1,18 @@ +// Package ccommon holds portability spellings shared by C's two emitters. +package ccommon + +// Align16 makes the C lane pair honor the table/cook layout's 16-byte width +// and alignment, including when a packet type supplies table storage. +const Align16 = ` +#ifndef SCHEMA_C_ALIGN16 +#if defined(_MSC_VER) +#define SCHEMA_C_ALIGN16 __declspec(align(16)) +#elif defined(__GNUC__) || defined(__clang__) +#define SCHEMA_C_ALIGN16 __attribute__((aligned(16))) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define SCHEMA_C_ALIGN16 _Alignas(16) +#else +#error "table 128-bit storage requires a compiler with 16-byte alignment support" +#endif +#endif +` diff --git a/internal/codegen/ctable/allocator.go b/internal/codegen/ctable/allocator.go new file mode 100644 index 000000000..1f809a240 --- /dev/null +++ b/internal/codegen/ctable/allocator.go @@ -0,0 +1,36 @@ +package ctable + +const tableAllocatorRuntime = ` +#ifndef SCHEMA_TABLE_ALLOCATOR_RUNTIME +#define SCHEMA_TABLE_ALLOCATOR_RUNTIME +#ifndef schema_allocate +#include +#define schema_allocate(bytes) calloc(1,(size_t)(bytes)) +#endif +#ifndef schema_release +#include +#define schema_release(pointer) free(pointer) +#endif +typedef struct TableAllocator +{ + void * (*alloc)(void * context,int64_t bytes); + void (*free)(void * context,void * pointer); + void * context; +} TableAllocator; +static SCHEMA_UNUSED void * table_default_allocate(void * context,int64_t bytes) +{ (void)context; return bytes>0 && (uint64_t)bytes<=SIZE_MAX ? schema_allocate(bytes) : NULL; } +static SCHEMA_UNUSED void table_default_release(void * context,void * pointer) +{ (void)context; schema_release(pointer); } +static SCHEMA_UNUSED TableAllocator table_default_allocator(void) +{ + TableAllocator allocator={table_default_allocate,table_default_release,NULL}; return allocator; +} +static SCHEMA_UNUSED void * table_allocate(TableAllocator allocator,int64_t bytes) +{ + if(allocator.alloc==NULL || allocator.free==NULL || bytes<=0 || (uint64_t)bytes>SIZE_MAX) { return NULL; } + return allocator.alloc(allocator.context,bytes); +} +static SCHEMA_UNUSED void table_release(TableAllocator allocator,void * pointer) +{ if(allocator.free!=NULL) { allocator.free(allocator.context,pointer); } } +#endif +` diff --git a/internal/codegen/ctable/arena.go b/internal/codegen/ctable/arena.go index f61d20f9b..b1acf25e5 100644 --- a/internal/codegen/ctable/arena.go +++ b/internal/codegen/ctable/arena.go @@ -196,10 +196,11 @@ typedef struct TableArena { TableAtomicPtr segments[ kTableMaxSegments ]; TableAtomicU32 cursor; /* (segment << kTableSegmentBits) | bytes handed out */ + TableAllocator allocator; int locked; /* MONOTONIC: Lock is one-way, there is no unlock */ } TableArena; -static SCHEMA_UNUSED void table_arena_init( TableArena * arena ) +static SCHEMA_UNUSED void table_arena_init_with_allocator( TableArena * arena, TableAllocator allocator ) { uint32_t i; for ( i = 0; i < kTableMaxSegments; i++ ) @@ -207,9 +208,12 @@ static SCHEMA_UNUSED void table_arena_init( TableArena * arena ) table_atomic_store_ptr( &arena->segments[i], (uint8_t *) NULL ); } table_atomic_store32( &arena->cursor, 0 ); - arena->locked = 0; + arena->locked = 0; arena->allocator=allocator; } +static SCHEMA_UNUSED void table_arena_init(TableArena * arena) +{ table_arena_init_with_allocator(arena,table_default_allocator()); } + static SCHEMA_UNUSED void table_arena_shutdown( TableArena * arena ) { uint32_t i; @@ -219,7 +223,7 @@ static SCHEMA_UNUSED void table_arena_shutdown( TableArena * arena ) if ( segment != NULL ) { uint8_t * expected = segment; - if ( table_arena_cas_ptr( &arena->segments[i], &expected, (uint8_t *) NULL ) ) { free( segment ); } + if ( table_arena_cas_ptr( &arena->segments[i], &expected, (uint8_t *) NULL ) ) { table_release(arena->allocator,segment); } } } table_atomic_store32( &arena->cursor, 0 ); @@ -254,12 +258,12 @@ static SCHEMA_UNUSED uint32_t table_arena_grab_slab( TableArena * arena ) region. It costs nothing measurable: a fresh segment is untouched pages either way, and calloc has the kernel hand them over zeroed. */ - uint8_t * memory = (uint8_t *) calloc( 1, kTableSegmentSize ); + uint8_t * memory = (uint8_t *) table_allocate(arena->allocator,kTableSegmentSize); uint8_t * expected = NULL; if ( memory == NULL ) { return kTableAllocFailed; } if ( !table_arena_cas_ptr( &arena->segments[segment], &expected, memory ) ) { - free( memory ); /* another worker published this segment first */ + table_release(arena->allocator,memory); /* another worker published this segment first */ } } if ( table_arena_cas32( &arena->cursor, &cursor, cursor + kTableSlabBytes ) ) @@ -305,24 +309,44 @@ static SCHEMA_UNUSED TableWorker table_worker_make( TableArena * arena ) arena offset, or kTableAllocFailed. It is the untyped half of every generated _emplace: the type's own size and its reset stay in the generated code, where they can be spelled. */ -static SCHEMA_UNUSED uint32_t table_worker_bump( TableWorker * worker, uint32_t bytes ) +static SCHEMA_UNUSED uint32_t table_arena_grab_span( TableArena * arena, int64_t bytes ) +{ + uint32_t spanned; + if(bytes<=0 || bytes>((int64_t)kTableMaxSegments-2)*kTableSegmentSize) { return kTableAllocFailed; } + spanned=(uint32_t)((bytes+kTableSegmentSize-1)>>kTableSegmentBits); + for(;;) + { + uint32_t cursor=table_atomic_load32(&arena->cursor); + uint32_t start=(cursor>>kTableSegmentBits)+1; + uint32_t next; + uint8_t * memory; + if(start+spanned>=kTableMaxSegments) { return kTableAllocFailed; } + next=(start+spanned)<cursor,&cursor,next)) { continue; } + memory=(uint8_t *)table_allocate(arena->allocator,bytes); + if(memory==NULL) { return kTableAllocFailed; } + table_atomic_store_ptr(&arena->segments[start],memory); + return start<arena == NULL || worker->arena->locked ) { return kTableAllocFailed; } - bytes = table_align_up( bytes ); - if ( bytes > kTableSlabBytes ) { return kTableAllocFailed; } /* a node larger than a slab: refused, never split */ - if ( worker->end == 0 || worker->next + bytes > worker->end ) + if(worker->arena==NULL || worker->arena->locked || bytes<=0 || bytes>INT64_MAX-kTableAlign) { return kTableAllocFailed; } + bytes=table_align_up64(bytes); + if(bytes>kTableSlabBytes) { return table_arena_grab_span(worker->arena,bytes); } + if(worker->end==0 || (uint64_t)worker->next+(uint64_t)bytes>worker->end || (worker->next==0 && bytes+kTableAlign>kTableSlabBytes)) { - uint32_t offset = table_arena_grab_slab( worker->arena ); - if ( offset == kTableAllocFailed ) { return kTableAllocFailed; } - worker->next = offset; - worker->end = offset + kTableSlabBytes; - if ( worker->next == 0 ) { worker->next = kTableAlign; } /* offset 0 is null: the arena's head stays reserved */ + uint32_t offset=table_arena_grab_slab(worker->arena); + if(offset==kTableAllocFailed) { return kTableAllocFailed; } + worker->next=offset; worker->end=offset+kTableSlabBytes; + if(worker->next==0) { worker->next=kTableAlign; } + if((uint64_t)worker->next+(uint64_t)bytes>worker->end) { return table_arena_grab_span(worker->arena,bytes); } } - at = worker->next; - worker->next += bytes; - return at; + at=worker->next; worker->next+=(uint32_t)bytes; return at; } +static SCHEMA_UNUSED uint32_t table_worker_bump( TableWorker * worker, uint32_t bytes ) +{ return table_worker_alloc(worker,bytes); } /* ---- the resolution context: which encoding a walk is reading ---- diff --git a/internal/codegen/ctable/blobs.go b/internal/codegen/ctable/blobs.go new file mode 100644 index 000000000..23ad14170 --- /dev/null +++ b/internal/codegen/ctable/blobs.go @@ -0,0 +1,92 @@ +package ctable + +import ( + "fmt" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +// Blobs are graph nodes. Their wire body is the used bytes; their storage has +// an eight-byte header and, for text, a terminator outside the wire extent. +func tableBlobRuntime() string { + return fmt.Sprintf(` +#ifndef SCHEMA_TABLE_BLOB_RUNTIME +#define SCHEMA_TABLE_BLOB_RUNTIME +typedef struct TableBlob { uint32_t length, zero; } TableBlob; +typedef struct TableBytesView { const uint8_t * data; int64_t length; } TableBytesView; +typedef struct TableStringView { const char * data; int64_t length; } TableStringView; +#define kTableBytesTypeId UINT64_C(0x%016x) +#define kTableStringTypeId UINT64_C(0x%016x) + +static SCHEMA_UNUSED int64_t table_blob_storage( int64_t length, int terminated ) +{ + if ( length < 0 || (uint64_t)length > UINT32_MAX ) { return -1; } + return table_align_up64(8+length+(terminated ? 1 : 0)); +} + +static SCHEMA_UNUSED const TableBlob * table_blob_at( const TableCtx * ctx, const TableRef * ref ) +{ + if ( ref->value == 0 ) { return NULL; } + return (const TableBlob *)(const void *)(ctx != NULL && ctx->arena != NULL + ? table_arena_at(ctx->arena,(uint32_t)ref->value) : (const uint8_t *)(const void *)ref+ref->value); +} +static SCHEMA_UNUSED TableBytesView table_bytes_at( const TableCtx * ctx, const TableRef * ref ) +{ + const TableBlob * blob=table_blob_at(ctx,ref); + TableBytesView view={NULL,0}; + if(blob!=NULL) { view.data=(const uint8_t *)(blob+1); view.length=blob->length; } return view; +} +static SCHEMA_UNUSED TableStringView table_string_at( const TableCtx * ctx, const TableRef * ref ) +{ + const TableBlob * blob=table_blob_at(ctx,ref); + TableStringView view={NULL,0}; + if(blob!=NULL) { view.data=(const char *)(blob+1); view.length=blob->length; } return view; +} +static SCHEMA_UNUSED TableBlob * table_blob_emplace( TableWorker * worker, TableRef * ref, int64_t length, int terminated ) +{ + int64_t bytes=table_blob_storage(length,terminated); + uint32_t at; + TableBlob * blob; + if(bytes<0) { return NULL; } + at=table_worker_alloc(worker,bytes); + if(at==kTableAllocFailed) { return NULL; } + blob=(TableBlob *)(void *)table_arena_at(worker->arena,at); + blob->length=(uint32_t)length; blob->zero=0; ref->value=at; return blob; +} +static SCHEMA_UNUSED uint8_t * table_bytes_emplace( TableWorker * worker, TableRef * ref, int64_t length ) +{ + TableBlob * blob=table_blob_emplace(worker,ref,length,0); + return blob!=NULL ? (uint8_t *)(blob+1) : NULL; +} +static SCHEMA_UNUSED char * table_string_emplace( TableWorker * worker, TableRef * ref, const char * text, int64_t length ) +{ + TableBlob * blob=table_blob_emplace(worker,ref,length,1); + char * data; + if(blob==NULL) { return NULL; } + data=(char *)(blob+1); if(text!=NULL && length>0) { memcpy(data,text,(size_t)length); } data[length]=0; return data; +} +#endif +`, ir.BytesWireTypeId, ir.StringWireTypeId) +} + +const tableBlobGraphRuntime = ` +static SCHEMA_UNUSED int table_blob_save( TableWriter * w, const void * value ) +{ + const TableBlob * blob=(const TableBlob *)value; + table_writer_raw(w,blob+1,blob->length); return !w->overflow; +} +static SCHEMA_UNUSED int table_blob_edges( TableNumbering * n, const void * value ) +{ (void)n; (void)value; return 1; } +static const TableNodeType table_bytes_node_type = { kTableBytesTypeId, 0, table_blob_save, table_blob_edges, 1, NULL, NULL, 8, 0, NULL, NULL }; +static const TableNodeType table_string_node_type = { kTableStringTypeId, 0, table_blob_save, table_blob_edges, 2, NULL, NULL, 8, 0, NULL, NULL }; +` + +func (g *tableGen) graphFieldType(f *ir.Field) string { + if f.Type.Blob() { + if f.Type.Kind == ir.TString { + return "table_string_node_type" + } + return "table_bytes_node_type" + } + return g.graphType(f.Type.Name) +} diff --git a/internal/codegen/ctable/block.go b/internal/codegen/ctable/block.go index eb04b7ba4..0aa902b5e 100644 --- a/internal/codegen/ctable/block.go +++ b/internal/codegen/ctable/block.go @@ -40,7 +40,7 @@ import ( // own and one text — buildVersionConstant, emitted by both. func blockRuntime(pkg string) string { guard := "SCHEMA_" + strings.ToUpper(pkg) + "_BLOCK_PRIMITIVES" - return `#ifndef ` + guard + ` + return tableAllocatorRuntime + `#ifndef ` + guard + ` #define ` + guard + ` /* ---- the block form's runtime (docs/SPEC-TABLES.md §19) ---- */ @@ -64,17 +64,12 @@ typedef struct TableBlockTriple The FILL path allocates nothing and takes no lock, which is the obligation §19.1 states; "the block form never allocates" would be a claim about who owns the extent, and the answer is that the caller does. */ -typedef struct TableBlockAllocator -{ - void * ( *alloc )( void * context, int64_t bytes ); - void ( *free )( void * context, void * pointer ); - void * context; -} TableBlockAllocator; +typedef TableAllocator TableBlockAllocator; /* The default triple, for a caller that has no allocator of its own to hand in. Nothing in the generated surface reaches for it: a caller names it. */ -static SCHEMA_UNUSED void * table_block_default_alloc( void * context, int64_t bytes ) { (void) context; return malloc( (size_t) bytes ); } -static SCHEMA_UNUSED void table_block_default_free( void * context, void * pointer ) { (void) context; free( pointer ); } +static SCHEMA_UNUSED void * table_block_default_alloc( void * context, int64_t bytes ) { (void) context; return bytes>0 && (uint64_t)bytes<=SIZE_MAX ? schema_allocate(bytes) : NULL; } +static SCHEMA_UNUSED void table_block_default_free( void * context, void * pointer ) { (void) context; schema_release( pointer ); } static SCHEMA_UNUSED TableBlockAllocator table_block_default_allocator( void ) { @@ -123,6 +118,10 @@ static SCHEMA_UNUSED void * table_block_row_at( const TableBlockRows * rows, int return (void *) ( rows->base + (ptrdiff_t) index * rows->stride ); } +typedef struct TableBlockConstRows { const uint8_t * base; int32_t count, stride; } TableBlockConstRows; +static SCHEMA_UNUSED const void * table_block_row_at_const(const TableBlockConstRows * rows,int32_t index) +{ return rows->base+(ptrdiff_t)index*rows->stride; } + /* ---- reflection over a block (docs/SPEC-TABLES.md §8, §19.2) ---- The descriptors are the mechanism, and they are what retires a hand-kept @@ -203,6 +202,9 @@ static SCHEMA_UNUSED uint64_t table_block_read64( const uint8_t * p ) return v; } +static SCHEMA_UNUSED uint32_t table_block_read32(const uint8_t * p) +{ uint32_t value; memcpy(&value,p,4); return value; } + static SCHEMA_UNUSED int64_t table_block_align( int64_t offset, int64_t alignment ) { return ( offset + alignment - 1 ) / alignment * alignment; @@ -401,6 +403,13 @@ func (g *tableGen) emitBlockSurface(bl *ir.BlockLayout) { g.pf(" %sBlockProjection * projection; /* the projection, at offset 0 */\n", name) g.pf(" int64_t bytes; /* the extent in use */\n") g.pf("} %sBlock;\n\n", name) + g.pf("typedef struct %sBlockConst { const uint8_t * base; const %sBlockProjection * projection; int64_t bytes; } %sBlockConst;\n", name, name, name) + g.pf("static SCHEMA_UNUSED int64_t %s(const %sBlockConst * block) { return block->bytes; }\n", g.api(name, "block_bytes_const"), name) + for _, a := range bl.Arrays { + field := g.api(name, ir.RustSnake(a.Field.Name)) + g.pf("static SCHEMA_UNUSED TableBlockConstRows %s_rows_const(const %sBlockConst * block)\n{ TableBlockConstRows rows; rows.base=block->base+block->projection->%s.offset_of; rows.count=(int32_t)block->projection->%s.count; rows.stride=(int32_t)block->projection->%s.stride; return rows; }\n", field, name, a.Field.Name, a.Field.Name, a.Field.Name) + g.pf("static SCHEMA_UNUSED const %s * %s_span_const(const %sBlockConst * block)\n{ return (const %s *)(const void *)(block->base+block->projection->%s.offset_of); }\n", a.ElemName, field, name, a.ElemName, a.Field.Name) + } g.pf("/* this table's block descriptors (docs/SPEC-TABLES.md §8, §19.2): constant\n") g.pf(" data, defined in the .c beside this header. A consumer holding this reads\n") @@ -431,6 +440,11 @@ func (g *tableGen) emitBlockSurface(bl *ir.BlockLayout) { g.pf("static SCHEMA_UNUSED int %s( %sBlock * block, void * base, int64_t bytes )\n{\n", g.api(name, "block_open"), name) g.pf(" return %s( block, base, bytes );\n}\n\n", g.sym(name, "block_open")) + g.pf("int %s(%sBlock * block,void * base,int64_t bytes,TableRefuseReason * reason);\n", g.sym(name, "block_open_ex"), name) + g.pf("static SCHEMA_UNUSED int %s(%sBlock * block,void * base,int64_t bytes,TableRefuseReason * reason)\n{ return %s(block,base,bytes,reason); }\n", g.api(name, "block_open_ex"), name, g.sym(name, "block_open_ex")) + g.pf("int %s(%sBlockConst * block,const void * base,int64_t bytes,TableRefuseReason * reason);\n", g.sym(name, "block_open_const_ex"), name) + g.pf("static SCHEMA_UNUSED int %s(%sBlockConst * block,const void * base,int64_t bytes,TableRefuseReason * reason)\n{ return %s(block,base,bytes,reason); }\n", g.api(name, "block_open_const_ex"), name, g.sym(name, "block_open_const_ex")) + g.pf("static SCHEMA_UNUSED int %s(%sBlockConst * block,const void * base,int64_t bytes)\n{ return %s(block,base,bytes,NULL); }\n", g.api(name, "block_open_const"), name, g.sym(name, "block_open_const_ex")) g.pf("/* ---- the block form of table %s: end ---- */\n\n", name) } @@ -636,63 +650,27 @@ func (g *tableGen) emitBlockDefinitions(bl *ir.BlockLayout) { func (g *tableGen) emitBlockOpenBody(bl *ir.BlockLayout) { name := bl.Table.Name - g.pf("int %s( %sBlock * block, void * base, int64_t bytes )\n{\n", g.sym(name, "block_open"), name) - g.pf(" const uint8_t * raw;\n uint64_t magic;\n %sBlockProjection * projection;\n int64_t used, padding;\n", name) - g.pf(" block->base = NULL;\n block->projection = NULL;\n block->bytes = 0;\n") - g.pf(" if ( base == NULL || bytes < %d ) { return 0; }\n", bl.Projection.Size) - g.pf(" if ( ( (uintptr_t) base %% %d ) != 0 ) { return 0; } /* the base's alignment */\n", ir.BlockAlign) - g.pf(" raw = (const uint8_t *) base;\n") - g.pf(" magic = table_block_read64( raw );\n") - g.pf(" if ( magic != table_block_magic )\n") - g.pf(" {\n") - g.pf(" /* a byte-swapped magic is a FOREIGN BYTE ORDER, and anything else is\n") - g.pf(" not a block at all. Both refuse; the distinction is here so a\n") - g.pf(" reader of this code knows the check covers the order too. */\n") - g.pf(" (void) table_block_byteswap64( magic );\n") - g.pf(" return 0;\n") - g.pf(" }\n") - g.pf(" if ( table_block_read64( raw + 8 ) != %s ) { return 0; }\n", buildVersionName(g.unit.Package)) - g.pf(" if ( table_block_read64( raw + 16 ) != table_block_byte_order )\n") - g.pf(" {\n return 0; /* a block of the other byte order: the fix-up path is a named obligation */\n }\n") - g.pf(" projection = (%sBlockProjection *) base;\n", name) - g.pf(" used = %d;\n", bl.Projection.Size) + check := g.sym(name, "block_open_check") + g.pf("static int %s(const void * base,int64_t bytes,int64_t * used_out,TableRefuseReason * reason)\n{\n const uint8_t * raw=(const uint8_t *)base; uint64_t magic; int64_t used=%d, padding;\n", check, bl.Projection.Size) + g.pf(" if(base==NULL) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE)!=NULL; }\n if(bytes<%d) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED)!=NULL; }\n", bl.Projection.Size) + g.pf(" magic=table_block_read64(raw);\n if(magic!=table_block_magic) { return table_cook_refuse(reason,magic==table_block_byteswap64(table_block_magic) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK)!=NULL; }\n") + g.pf(" if(table_block_read64(raw+16)!=table_block_byte_order) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK)!=NULL; }\n if(table_block_read64(raw+8)!=%s) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION)!=NULL; }\n", buildVersionName(g.unit.Package)) for _, a := range bl.Arrays { - g.pf(" {\n") - g.pf(" /* EVERY NUMBER BELOW COMES FROM THE INSTANCE, so the arithmetic is\n") - g.pf(" unsigned and each term is BOUNDED BEFORE IT IS ADDED. A forged\n") - g.pf(" offset_of near 2^63 must refuse, and an addition that carried past\n") - g.pf(" the top of the type would be the undefined behaviour the check\n") - g.pf(" after it was supposed to catch. */\n") - g.pf(" const uint64_t offset_of = projection->%s.offset_of;\n", a.Field.Name) - g.pf(" const uint64_t count = projection->%s.count;\n", a.Field.Name) - g.pf(" const uint64_t stride = projection->%s.stride;\n", a.Field.Name) - g.pf(" uint64_t rows;\n int64_t end;\n") - g.pf(" if ( stride != (uint64_t) sizeof( %s ) ) { return 0; }\n", a.ElemName) - g.pf(" if ( count > (uint64_t) %d )\n", a.Max) - g.pf(" {\n") - g.pf(" return 0; /* past the DECLARED MAXIMUM: Begin refuses this on the\n") - g.pf(" producer side and Open refuses it here, because a\n") - g.pf(" consumer that sizes anything by the maximum would\n") - g.pf(" overflow on a count the maximum does not bound */\n") - g.pf(" }\n") - g.pf(" if ( offset_of < %d || ( offset_of %% %d ) != 0 ) { return 0; }\n", bl.Projection.Size, blockStartAlign(a)) - g.pf(" if ( offset_of > (uint64_t) bytes ) { return 0; }\n") - g.pf(" rows = count * stride; /* both bounded above: this cannot carry */\n") - g.pf(" if ( rows > (uint64_t) bytes - offset_of ) { return 0; }\n") - g.pf(" end = (int64_t) ( offset_of + rows );\n") - g.pf(" if ( end > used ) { used = end; }\n") - g.pf(" }\n") + g.pf(" { const uint8_t * triple=raw+%d; uint64_t offset=table_block_read64(triple), count=table_block_read32(triple+8), stride=table_block_read32(triple+12), rows; int64_t end;\n", a.OffsetOfOffset) + g.pf(" if(stride!=sizeof(%s) || count>%d || offset<%d || offset%%%d!=0 || offset>(uint64_t)bytes) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_LAYOUT)!=NULL; }\n", a.ElemName, a.Max, bl.Projection.Size, blockStartAlign(a)) + g.pf(" rows=count*stride; if(rows>(uint64_t)bytes-offset) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_LAYOUT)!=NULL; }\n end=(int64_t)(offset+rows); if(end>used) { used=end; }\n }\n") + } + g.pf(" padding=(%d-used%%%d)%%%d; if(padding>bytes-used) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED)!=NULL; }\n used+=padding; if((uintptr_t)base%%%d!=0) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE)!=NULL; }\n *used_out=used; return 1;\n}\n", ir.BlockAlign, ir.BlockAlign, ir.BlockAlign, ir.BlockAlign) + for _, readOnly := range []bool{false, true} { + suffix, typ, qual := "block_open_ex", "Block", "" + if readOnly { + suffix = "block_open_const_ex" + typ = "BlockConst" + qual = "const " + } + g.pf("int %s(%s%s * block,%svoid * base,int64_t bytes,TableRefuseReason * reason)\n{\n int64_t used=0; block->base=NULL; block->projection=NULL; block->bytes=0;\n if(!%s(base,bytes,&used,reason)) { return 0; }\n block->base=(%suint8_t *)base; block->projection=(%s%sBlockProjection *)base; block->bytes=used; return 1;\n}\n", g.sym(name, suffix), name, typ, qual, check, qual, qual, name) } - g.pf(" /* the used extent, rounded to %d WITHOUT the rounding itself carrying past\n", ir.BlockAlign) - g.pf(" the top of the type: used is already inside bytes, and the padding is\n") - g.pf(" paid out of the slack that is left rather than added and compared after. */\n") - g.pf(" padding = ( %d - ( used %% %d ) ) %% %d;\n", ir.BlockAlign, ir.BlockAlign, ir.BlockAlign) - g.pf(" if ( padding > bytes - used ) { return 0; }\n") - g.pf(" used += padding;\n") - g.pf(" block->base = (uint8_t *) base;\n") - g.pf(" block->projection = projection;\n") - g.pf(" block->bytes = used;\n") - g.pf(" return 1;\n}\n\n") + g.pf("int %s(%sBlock * block,void * base,int64_t bytes)\n{ return %s(block,base,bytes,NULL); }\n", g.sym(name, "block_open"), name, g.sym(name, "block_open_ex")) } // emitBlockDescriptors emits one table's block reflection: the projection diff --git a/internal/codegen/ctable/codecs.go b/internal/codegen/ctable/codecs.go index 6e40a6428..c8eea329f 100644 --- a/internal/codegen/ctable/codecs.go +++ b/internal/codegen/ctable/codecs.go @@ -18,7 +18,13 @@ import ( // this header read as one family. func (g *tableGen) cFieldType(t ir.FieldType) string { switch t.Kind { - case ir.TInt: + case ir.TInt, ir.TFixed: + if t.Width == 128 { + if t.Signed { + return "serialize_int128_t" + } + return "serialize_uint128_t" + } if t.Signed { return fmt.Sprintf("int%d_t", t.Width) } @@ -60,6 +66,9 @@ func cSelfInit(t ir.FieldType) bool { // tableIntLit renders an integer literal safely at 64-bit width: unsigned // values past INT64_MAX need ull, and INT64_MIN has no single-literal form. func tableIntLit(v *big.Int, signed bool, widthBytes int) string { + if widthBytes == 16 { + return tableWideLit(v, signed) + } s := v.String() if widthBytes < 8 { return s @@ -96,9 +105,13 @@ func tableStorageRange(signed bool, bits int) (*big.Int, *big.Int) { // (-Wtautological-type-limit-compare), neither catching the other's // (issue #342, `make tables-clamp-limits`). func tableClampEnds(f *ir.Field, widthBytes int) (low, high bool) { - signed := f.Type.Kind == ir.TInt && f.Type.Signed + signed := ir.TableKindSigned(ir.TableScalarKind(f)) lo, hi := tableStorageRange(signed, widthBytes*8) - return f.IntMin.Cmp(lo) > 0, f.IntMax.Cmp(hi) < 0 + rlo, rhi, ok := ir.TableRawRange(f) + if !ok { + return false, false + } + return rlo.Cmp(lo) > 0, rhi.Cmp(hi) < 0 } // enumConst renders an enum variant's C spelling, the flat #define family the @@ -130,15 +143,21 @@ func (g *tableGen) fieldDefaultExpr(f *ir.Field) string { return formatFloat(f.DefFloat, false) } return "0.0" - case ir.TInt, ir.TBits: + case ir.TInt, ir.TBits, ir.TFixed: if f.HasDefault && f.DefInt != nil { - signed := f.Type.Kind == ir.TInt && f.Type.Signed + signed := ir.TableKindSigned(ir.TableScalarKind(f)) width := 4 if f.Type.Width > 32 { width = 8 } + if f.Type.Width == 128 { + width = 16 + } return tableIntLit(f.DefInt, signed, width) } + if f.Type.Width == 128 { + return tableWideLit(big.NewInt(0), f.Type.Signed) + } return "0" case ir.TNamed: switch f.Type.Ref.(type) { @@ -148,6 +167,9 @@ func (g *tableGen) fieldDefaultExpr(f *ir.Field) string { } return enumNoneConst(f.Type.Name) case *ir.Flags: + if f.HasDefault && f.DefInt != nil { + return tableIntLit(f.DefInt, false, 8) + } return "0" } } @@ -183,16 +205,36 @@ func (g *tableGen) emitTableStruct(st *ir.Struct) { } func (g *tableGen) emitTableStorageField(f *ir.Field) { + if f.IsList() { + g.pf(" TableList %s;\n", f.Name) + return + } + if f.IsMap() { + g.pf(" TableMap %s;\n", f.Name) + return + } if f.Type.Pointer { // a pointer is EIGHT BYTES and no address: an arena offset while the // builder is mutable, a self-relative delta once packed. That is what // keeps a pointer-bearing table relocatable in both forms. g.noteRef(f.Type.Name) - g.pf(" TableRef %s; /* *%s — null until assigned */\n", f.Name, f.Type.Name) + if f.Array == ir.ArrayNone { + g.pf(" TableRef %s; /* *%s — null until assigned */\n", f.Name, f.Type.Name) + } else { + g.pf(" TableRef %s[%d];\n", f.Name, f.ArrayBound) + if f.Array == ir.ArrayCounted { + g.pf(" int32_t %s_count;\n", f.Name) + } + } return } typ := g.cFieldType(f.Type) + if f.Type.Width == 128 { + typ = "SCHEMA_C_ALIGN16 " + typ + } switch { + case f.Type.Kind == ir.TWString: + g.pf(" uint16_t %s[%d + 1];\n int32_t %s_length;\n", f.Name, f.Type.Size, f.Name) case f.Type.Kind == ir.TString: g.pf(" char %s[%d + 1]; /* string(%d): N + 1 for the terminator the wire does not carry */\n", f.Name, f.Type.Size, f.Type.Size) g.pf(" int32_t %s_length;\n", f.Name) @@ -255,16 +297,30 @@ func (g *tableGen) emitTableReset(st *ir.Struct) { } func (g *tableGen) emitTableResetField(f *ir.Field) { + if f.IsList() || f.IsMap() { + g.pf(" memset(&value->%s,0,sizeof(value->%s));\n", f.Name, f.Name) + return + } if f.Type.Pointer { - g.pf(" value->%s.value = 0; /* *%s — null */\n", f.Name, f.Type.Name) + if f.Array == ir.ArrayNone { + g.pf(" value->%s.value = 0; /* *%s — null */\n", f.Name, f.Type.Name) + } else { + g.pf(" memset(value->%s,0,sizeof(value->%s));\n", f.Name, f.Name) + if f.Array == ir.ArrayCounted { + g.pf(" value->%s_count=0;\n", f.Name) + } + } return } typ := g.cFieldType(f.Type) selfInit := cSelfInit(f.Type) switch { - case f.Type.Kind == ir.TString, f.Type.Kind == ir.TBytes: + case f.Type.Kind == ir.TString, f.Type.Kind == ir.TWString, f.Type.Kind == ir.TBytes: g.pf(" memset( value->%s, 0, sizeof( value->%s ) );\n", f.Name, f.Name) - g.pf(" value->%s_length = 0;\n", f.Name) + if f.HasDefault && len(f.DefBytes) != 0 { + g.pf(" { static const uint8_t initial[] = { %s }; memcpy( value->%s, initial, sizeof( initial ) ); }\n", byteLiterals(f.DefBytes), f.Name) + } + g.pf(" value->%s_length = %d;\n", f.Name, len(f.DefBytes)) case f.KeyEnum != "": g.emitTableResetArray("value->"+f.Name, enumMaxConst(f.KeyEnum), typ, selfInit, f) case f.Array == ir.ArrayFixed: @@ -1154,20 +1210,20 @@ func (g *tableGen) emitTableReadScalarFrom(f *ir.Field, kind int, lvalue, ind, r case tkF64: g.pf("%s%s = table_bits_to_double( table_reader_get64( &%s ) );\n", ind, lvalue, rdr) default: - signed := f.Type.Kind == ir.TInt && f.Type.Signed + signed := ir.TableKindSigned(ir.TableScalarKind(f)) storage := fmt.Sprintf("uint%d_t", width*8) if signed { storage = fmt.Sprintf("int%d_t", width*8) } g.pf("%s{\n%s %s decoded_v = (%s) %s( &%s );\n", ind, ind, storage, storage, tableGet(width), rdr) - if f.HasIntRange { + if rlo, rhi, ok := ir.TableRawRange(f); ok { low, high := tableClampEnds(f, width) if low { - lo := tableIntLit(f.IntMin, signed, width) + lo := tableIntLit(rlo, signed, width) g.pf("%s if ( decoded_v < %s ) { decoded_v = %s; r->report->clamped++; }\n", ind, lo, lo) } if high { - hi := tableIntLit(f.IntMax, signed, width) + hi := tableIntLit(rhi, signed, width) lead := "if" if low { lead = "else if" @@ -1187,31 +1243,7 @@ func (g *tableGen) emitTableReadScalarFrom(f *ir.Field, kind int, lvalue, ind, r // tableFieldTypeName renders a field's schema-facing type name for the // descriptor ("float32", "bits(9)", "Grade", "GunnerSettings"). -func tableFieldTypeName(f *ir.Field) string { - switch f.Type.Kind { - case ir.TBool: - return "bool" - case ir.TInt: - prefix := "int" - if !f.Type.Signed { - prefix = "uint" - } - return fmt.Sprintf("%s%d", prefix, f.Type.Width) - case ir.TBits: - return fmt.Sprintf("bits(%d)", f.Type.Width) - case ir.TFloat32: - return "float32" - case ir.TFloat64: - return "float64" - case ir.TString: - return "string" - case ir.TBytes: - return "bytes" - case ir.TNamed: - return f.Type.Name - } - return "?" -} +func tableFieldTypeName(f *ir.Field) string { return ir.TableTypeSpelling(f) } // bigToDouble renders a big.Int as a C double literal for the descriptor's // range fields (precision past 2^53 is documented as lost). @@ -1226,6 +1258,9 @@ func bigToDouble(v *big.Int) string { // underscore. Nothing a schema can declare collides with it, which is why the // vocabularies claim no name of their own (docs/SPEC-TABLES.md §11). func (g *tableGen) vocabularySymbol(owner, field, what string) string { + if g.outside { + what = "outside_" + what + } return g.sym(owner, ir.RustSnake(field)+"_"+what) } @@ -1296,6 +1331,16 @@ func annotationColumns(doc string, tags []string, tagsName string) string { // emitFieldVocabulary emits one field's variant table, its key table and its // union arm table where it has them. func (g *tableGen) emitFieldVocabulary(st *ir.Struct, f *ir.Field) { + if ir.TableScalarKind(f) >= 18 && ir.TableScalarKind(f) <= 29 { + lo, hi, ok := ir.TableRawRange(f) + if !ok { + lo, hi = tableStorageRange(f.Type.Signed, f.Type.Width) + } + llo, lhi := wideLanes(lo) + hlo, hhi := wideLanes(hi) + g.pf("static const TableWideRange %s = { { %sull, %sull }, { %sull, %sull } };\n", g.vocabularySymbol(st.Name, f.Name, "range"), llo, lhi, hlo, hhi) + } + switch ref := f.Type.Ref.(type) { case *ir.Enum: if f.Type.Kind != ir.TNamed { @@ -1308,15 +1353,18 @@ func (g *tableGen) emitFieldVocabulary(st *ir.Struct, f *ir.Field) { } for i := int64(0); i <= ref.Max; i++ { if n, ok := names[i]; ok { - id := uint16(0) + id := uint64(0) if i != 0 { - id = ir.VariantId(n) + id = uint64(ir.VariantId(n)) + if g.fileWire() { + id = ir.TableWireId(ref.VariantWireName(int(i - 1))) + } } - g.pf(" { \"%s\", 0x%04x },\n", n, id) + g.pf(" { \"%s\", 0x%016xull },\n", n, g.descriptorID(id)) continue } // headroom past the declared set: a value the vocabulary does not - // name, and the reserved id 0 no declared name can fold to (§5) + // name; distinguish it by its missing name, not by its identity g.pf(" { NULL, 0 },\n") } g.pf("};\n") @@ -1333,20 +1381,35 @@ func (g *tableGen) emitFieldVocabulary(st *ir.Struct, f *ir.Field) { } g.pf("};\n") case *ir.Union: - if f.Type.Kind != ir.TNamed || f.Array != ir.ArrayNone { + if f.Type.Kind != ir.TNamed { break } + g.emitUnionArmDescriptors(ref) g.pf("static const TableVariantInfo %s[] = {\n", g.vocabularySymbol(st.Name, f.Name, "variants")) g.pf(" { \"None\", 0 },\n") for _, v := range ref.Variants { - g.pf(" { \"%s\", 0x%04x },\n", v.Name, ir.VariantId(v.Name)) + id := uint64(ir.VariantId(v.Name)) + if g.fileWire() { + id = ir.TableWireId(v.WireName()) + } + g.pf(" { \"%s\", 0x%016xull },\n", v.Name, g.descriptorID(id)) } g.pf("};\n") g.pf("static const TableUnionArmInfo %s[] = {\n", g.vocabularySymbol(st.Name, f.Name, "arms")) - g.pf(" { 0, NULL },\n") + g.pf(" { 0, NULL, NULL, 0 },\n") for _, v := range ref.Variants { - g.noteRef(v.Type) - g.pf(" { (uint32_t) offsetof( %s, as.%s ), &%s },\n", ref.Name, v.Name, g.sym(v.Type, "info")) + if v.Void() { + g.pf(" { 0, NULL, NULL, 0 },\n") + continue + } + target, field := "NULL", "NULL" + if v.Body() { + g.noteRef(v.Type) + target = "&" + g.sym(v.Type, "info") + } else { + field = g.armDescriptorSymbol(ref.Name, v.Name) + } + g.pf(" { (uint32_t)offsetof( %s, as.%s ), %s, %s, (uint32_t)sizeof( ((%s *)0)->as.%s ) },\n", ref.Name, v.Name, target, field, ref.Name, v.Name) } g.pf("};\n") g.pf("static const TableUnionInfo %s = { (uint32_t) offsetof( %s, type ), (uint32_t) sizeof( ( (%s *) 0 )->type ), %s };\n", @@ -1364,11 +1427,14 @@ func (g *tableGen) emitFieldVocabulary(st *ir.Struct, f *ir.Field) { } for i := int64(0); i <= key.Max; i++ { if n, ok := names[i]; ok { - id := uint16(0) + id := uint64(0) if i != 0 { - id = ir.VariantId(n) + id = uint64(ir.VariantId(n)) + if g.fileWire() { + id = ir.TableWireId(key.VariantWireName(int(i - 1))) + } } - g.pf(" { \"%s\", 0x%04x },\n", n, id) + g.pf(" { \"%s\", 0x%016xull },\n", n, g.descriptorID(id)) continue } g.pf(" { NULL, 0 },\n") @@ -1378,16 +1444,25 @@ func (g *tableGen) emitFieldVocabulary(st *ir.Struct, f *ir.Field) { } func (g *tableGen) emitFieldDescriptor(st *ir.Struct, f *ir.Field, guard string) { - id := ir.TableFieldId(f) + g.emitFieldDescriptorAt(st, f, guard, f.Name) +} +func (g *tableGen) emitFieldDescriptorAt(st *ir.Struct, f *ir.Field, guard, member string) { + id := uint64(ir.TableFieldId(f)) + if g.fileWire() { + id = ir.TableFieldWireId(f) + } kind := tableScalarKind(f) - if f.Type.Kind == ir.TBytes { + if g.fileWire() { + kind = ir.TableWireScalarKind(f) + } + if f.Type.Kind == ir.TBytes && !f.Type.Blob() { kind = tkU8 } - isArray := f.Array != ir.ArrayNone || f.Type.Kind == ir.TBytes - if f.Type.Pointer { + isArray := f.Array != ir.ArrayNone || (f.Type.Kind == ir.TBytes && !f.Type.Blob()) + if f.Type.Pointer && !g.fileWire() { kind = tkTable } - counted := f.Array == ir.ArrayCounted || f.Type.Kind == ir.TBytes || f.Type.Kind == ir.TString + counted := f.Array == ir.ArrayCounted || f.Type.Kind == ir.TBytes || (f.Type.Kind == ir.TString || f.Type.Kind == ir.TWString) // the count column, spelled the way the storage spells its own extent: a // keyed array DERIVES it from the key enum, so nothing outside the array @@ -1398,28 +1473,28 @@ func (g *tableGen) emitFieldDescriptor(st *ir.Struct, f *ir.Field, guard string) bound = "(int32_t) " + enumMaxConst(f.KeyEnum) case f.Array != ir.ArrayNone: bound = strconv.FormatInt(f.ArrayBound, 10) - case f.Type.Kind == ir.TBytes, f.Type.Kind == ir.TString: + case f.Type.Kind == ir.TBytes, f.Type.Kind == ir.TString, f.Type.Kind == ir.TWString: bound = strconv.FormatInt(f.Type.Size, 10) } // `( (T *) 0 )->field`, never a materialised value: an unevaluated member // access names the type without an object, where a braced temporary makes // the compiler build a whole value of T to take the size of one member. - elemSize := fmt.Sprintf("(uint32_t) sizeof( ( (%s *) 0 )->%s )", st.Name, f.Name) + elemSize := fmt.Sprintf("(uint32_t) sizeof( ( (%s *) 0 )->%s )", st.Name, member) if isArray { - elemSize = fmt.Sprintf("(uint32_t) sizeof( ( (%s *) 0 )->%s[0] )", st.Name, f.Name) + elemSize = fmt.Sprintf("(uint32_t) sizeof( ( (%s *) 0 )->%s[0] )", st.Name, member) } countOffset := "0xffffffffu" if counted { - companion := f.Name + "_count" - if f.Type.Kind == ir.TBytes || f.Type.Kind == ir.TString { - companion = f.Name + "_length" + companion := member + "_count" + if f.Type.Kind == ir.TBytes || (f.Type.Kind == ir.TString || f.Type.Kind == ir.TWString) { + companion = member + "_length" } countOffset = fmt.Sprintf("(uint32_t) offsetof( %s, %s )", st.Name, companion) } - if f.Type.Pointer { + if f.Type.Pointer && f.Array == ir.ArrayNone { // a pointer's storage IS the reference slot: offset names it, // elem_size is the slot's width, and there is no companion elemSize = "(uint32_t) sizeof( TableRef )" @@ -1427,12 +1502,28 @@ func (g *tableGen) emitFieldDescriptor(st *ir.Struct, f *ir.Field, guard string) bound = "0" countOffset = "0xffffffffu" } + sequence := 0 + if f.IsList() || f.IsMap() { + sequence = 1 + if f.IsMap() { + sequence = 2 + } + isArray = true + counted = true + bound = "INT32_MAX" + elemSize = fmt.Sprintf("(uint32_t) sizeof(%s)", g.sequenceType(f)) + countOffset = fmt.Sprintf("(uint32_t) offsetof(%s,%s.count)", st.Name, member) + } table := "NULL" if _, isStruct := f.Type.Ref.(*ir.Struct); f.Type.Kind == ir.TNamed && isStruct { table = "&" + g.sym(f.Type.Name, "info") g.noteRef(f.Type.Name) } + if f.IsMap() { + table = "&" + g.sym(f.MapEntry.Name, "info") + kind = tkTable + } hasRange := "0" rangeMin, rangeMax := "0.0", "0.0" if f.Type.Kind == ir.TBits && !f.HasIntRange { @@ -1469,7 +1560,7 @@ func (g *tableGen) emitFieldDescriptor(st *ir.Struct, f *ir.Field, guard string) variants = g.vocabularySymbol(st.Name, f.Name, "variants") } case *ir.Union: - if f.Type.Kind == ir.TNamed && f.Array == ir.ArrayNone { + if f.Type.Kind == ir.TNamed { enumMax = fmt.Sprintf("%d", len(ref.Variants)) variants = g.vocabularySymbol(st.Name, f.Name, "variants") hasIds = "1" @@ -1479,7 +1570,7 @@ func (g *tableGen) emitFieldDescriptor(st *ir.Struct, f *ir.Field, guard string) presentOffset := "0xffffffffu" if f.Type.Optional { - presentOffset = fmt.Sprintf("(uint32_t) offsetof( %s, %s_present )", st.Name, f.Name) + presentOffset = fmt.Sprintf("(uint32_t) offsetof( %s, %s_present )", st.Name, member) } keyTypeName, keys, keyMax := "NULL", "NULL", "-1" @@ -1490,17 +1581,29 @@ func (g *tableGen) emitFieldDescriptor(st *ir.Struct, f *ir.Field, guard string) g.noteRef(f.KeyEnum) } + wide := "NULL" + frac := 0 + if ir.TableScalarKind(f) >= 18 && ir.TableScalarKind(f) <= 29 { + wide = "&" + g.vocabularySymbol(st.Name, f.Name, "range") + } + if f.Type.Kind == ir.TFixed { + frac = f.Type.FracBits + } + place := "NULL" + if f.IsMap() { + place = g.sym(f.MapEntry.Name, "json_place") + } pointerColumn := "" if g.anyVariable { pointerColumn = fmt.Sprintf("%s, ", boolC(f.Type.Pointer)) } - g.pf(" { \"%s\", \"%s\", \"%s\", 0x%04x, %d, %s, %s%s, %s, %s, (uint32_t) offsetof( %s, %s ), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, \"%s\", %s },\n", - f.Name, ir.TableFieldJsonKey(f), tableFieldTypeName(f), id, kind, boolC(isArray), pointerColumn, + g.pf(" { \"%s\", %s, \"%s\", 0x%016xull, %d, %s, %s%s, %s, %s, (uint32_t) offsetof( %s, %s ), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, \"%s\", %s, %d, %s },\n", + f.Name, g.descriptorJSON(f), tableFieldTypeName(f), g.descriptorID(id), kind, boolC(isArray), pointerColumn, boolC(counted), boolC(f.Type.Optional), bound, - st.Name, f.Name, elemSize, countOffset, presentOffset, table, - hasRange, rangeMin, rangeMax, enumMax, variants, hasIds, + st.Name, member, elemSize, countOffset, presentOffset, table, + hasRange, rangeMin, fmt.Sprintf("%s, %s, %d", rangeMax, wide, frac), enumMax, variants, hasIds, keyTypeName, keys, keyMax, arms, guard, - annotationColumns(f.Doc, f.Tags, g.vocabularySymbol(st.Name, f.Name, "tags"))) + annotationColumns(f.Doc, f.Tags, g.vocabularySymbol(st.Name, f.Name, "tags")), sequence, place) } func boolC(v bool) string { @@ -1526,3 +1629,27 @@ func boolC(v bool) string { // `Measure` is NOT force-inlined, in either class and in either backend: it is // called once per nested body to decide elision and its result is a number, so // it neither holds the cursor nor merges stores. + +func (g *tableGen) descriptorID(id uint64) uint64 { + if g.outside { + return 0 + } + return id +} +func (g *tableGen) descriptorJSON(f *ir.Field) string { + if g.outside { + return "NULL" + } + return fmt.Sprintf("%q", ir.TableFieldJsonKey(f)) +} + +// Each keyed accessor owns its declaration's bound, including after a caller +// receives the containing value through a pointer. Both arguments occur once. +func (g *tableGen) emitKeyedAccessors(st *ir.Struct) { + for _, f := range st.Fields { + if f.KeyEnum != "" { + name := "SCHEMA_" + ir.RustConstName(g.unit.Package) + "_" + ir.RustConstName(st.Name) + "_" + ir.RustConstName(f.Name) + "_AT" + g.pf("#define %s(value,key) SCHEMA_TABLE_KEYED_AT((value).%s,(key),%s)\n", name, f.Name, enumMaxConst(f.KeyEnum)) + } + } +} diff --git a/internal/codegen/ctable/cook.go b/internal/codegen/ctable/cook.go index 5f3a8d022..401e8f80b 100644 --- a/internal/codegen/ctable/cook.go +++ b/internal/codegen/ctable/cook.go @@ -78,6 +78,32 @@ func buildVersionConstant(pkg string, buildVersion uint64) string { ` } +// Keep the existing C int out-parameter ABI while naming the refusal domain. +const tableRefuseRuntime = ` +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif +` + // tableCookRuntime is the cooked form's shared read runtime, guarded per // package like the rest of the table runtime so one definition survives any // include order and a lone Table.h works standalone. @@ -175,25 +201,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != ` + buildVersionName(pkg) + ` ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != ` + buildVersionName(pkg) + ` ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -203,37 +229,39 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* ` + guard + ` */ ` @@ -249,7 +277,7 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 func (g *tableGen) emitCookSurface(members []*ir.Struct) { first := true for _, st := range members { - if !st.IsTable { + if !st.IsTable || st.IsMapEntry() { continue // a `type` is not a node and cannot be a cook's root } if first { @@ -288,6 +316,7 @@ func (g *tableGen) emitCookOpen(st *ir.Struct) { g.pf(" file whose provenance a person doubts is schema cook-check, offline, over\n") g.pf(" the ATTRIBUTION part beside the data — a person's decision, never a\n") g.pf(" parameter on a load. */\n") + g.pf("static SCHEMA_UNUSED const %s * %s(const void * bytes,uint64_t length,TableRefuseReason * reason)\n{ return (const %s *)table_cook_open_ex(bytes,length,sizeof(%s),SCHEMA_TABLE_ALIGNOF(%s),reason); }\n", n, g.api(n, "open_ex"), n, n, n) g.pf("static SCHEMA_UNUSED const %s * %s( const void * bytes, uint64_t length )\n{\n", n, g.api(n, "open")) g.pf(" return (const %s *) table_cook_open( bytes, length, (uint64_t) sizeof( %s ), (uint64_t) SCHEMA_TABLE_ALIGNOF( %s ) );\n}\n\n", n, n, n) } diff --git a/internal/codegen/ctable/cookwrite.go b/internal/codegen/ctable/cookwrite.go new file mode 100644 index 000000000..a0427ba44 --- /dev/null +++ b/internal/codegen/ctable/cookwrite.go @@ -0,0 +1,331 @@ +package ctable + +import ( + "fmt" + "github.com/mas-bandwidth/schema/v2/ir" + "slices" +) + +// Cooks are canonical artifacts. Native record padding and byte order never +// enter the file: zero the complete output and store each declared piece. +const tableCookWriteRuntime = ` +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(usedctx,slot); + uint64_t index=table_number_find(n,pointee); + if(pointee!=NULL && index==0) { return 0; } + table_cook_put(at,index ? n->entries[index-1].packed_offset-(uint64_t)(at-n->pack_base) : 0,8,order); + return 1; +} +static SCHEMA_UNUSED int64_t table_cook_layout(TableNumbering * n,int64_t * alignment,int64_t * data_bytes) +{ + uint64_t i; + int64_t total=0, align=8; + for(i=0;icount;i++) { + TableNodeEntry * entry=&n->entries[i]; const TableNodeType * type=entry->type; + int64_t size=type->size, node_align=type->alignment; + if(type->blob) { size=8+(int64_t)((const TableBlob *)entry->value)->length+(type->blob==2); } + else if(type->has_extent) { + int64_t extent=0; + if(!type->cook_extent(n,entry->value,NULL,NULL,&extent,TableByteOrder_Little) || size>INT64_MAX-7) { return -1; } + size=(size+7)&~INT64_C(7); if(extent>INT64_MAX-size) { return -1; } size+=extent; + } + if(node_align<1 || total>INT64_MAX-(node_align-1)) { return -1; } + total=(total+node_align-1)&~(node_align-1); + entry->packed_offset=(uint64_t)total; + if(size<0 || size>INT64_MAX-total) { return -1; } total+=size; + if(node_align>align) { align=node_align; } + } + if(total>INT64_MAX-(align-1)) { return -1; } total=(total+align-1)&~(align-1); + *alignment=align; *data_bytes=total; + if(total>INT64_MAX-64 || n->count>(uint64_t)(INT64_MAX-64-total)/16) { return -1; } + return 64+total+(int64_t)n->count*16; +} +static SCHEMA_UNUSED int64_t table_graph_cook(const TableCtx * ctx,const void * root,const TableNodeType * type, + void * output,uint64_t capacity,int order,TableAllocator allocator,uint64_t version,int measure) +{ + TableNumbering n; int64_t result=-1,align=8,bytes=0,need; uint64_t i; uint8_t * raw=(uint8_t *)output; + if(!table_number_build(&n,ctx,root,type,allocator)) { table_number_dispose(&n); return -1; } + need=table_cook_layout(&n,&align,&bytes); if(need<0) { goto done; } + if(measure) { result=need; goto done; } + if(raw==NULL || (uint64_t)need>capacity || (uint64_t)need>SIZE_MAX) { goto done; } + memset(raw,0,(size_t)need); n.pack_base=raw+64; + for(i=0;ipacked_offset; + if(entry->type->blob) { + const TableBlob * blob=(const TableBlob *)entry->value; + table_cook_put(at,blob->length,4,order); table_cook_bytes(at+8,blob+1,blob->length,blob->length); + } else { + int64_t extent=0; + if(!entry->type->cook_body(&n,at,entry->value,order)) { goto done; } + if(entry->type->has_extent) { + int64_t expected=0,record=(entry->type->size+7)&~INT64_C(7); + if(!entry->type->cook_extent(&n,entry->value,NULL,NULL,&expected,order) || + !entry->type->cook_extent(&n,entry->value,at,at+record,&extent,order) || extent!=expected) { goto done; } + } + } + table_cook_put(n.pack_base+bytes+i*16,entry->packed_offset,8,order); + table_cook_put(n.pack_base+bytes+i*16+8,entry->type->id,8,order); + } + /* A failed body must never leave a recognizable cook header. */ + table_cook_header(raw,version,bytes,(int64_t)n.count,align,order); result=need; +done: + table_number_dispose(&n); return result; +} +#endif +` + +func cookAlignUp(v, a int64) int64 { return (v + a - 1) / a * a } +func (g *tableGen) hasCookExtent(st *ir.Struct) bool { + return slices.ContainsFunc(st.Fields, g.cookFieldExtent) +} +func (g *tableGen) cookFieldExtent(f *ir.Field) bool { + if f.IsList() || f.IsMap() { + return true + } + if f.Type.Pointer { + return false + } + switch ref := f.Type.Ref.(type) { + case *ir.Struct: + return g.hasCookExtent(ref) + case *ir.Union: + for _, v := range ref.Variants { + if !v.Void() && g.cookFieldExtent(v.F) { + return true + } + } + } + return false +} +func (g *tableGen) cookBodySignature(st *ir.Struct) string { + return fmt.Sprintf("static SCHEMA_UNUSED int %s(struct TableNumbering * n,uint8_t * at,const void * storage,int order)", g.sym(st.Name, "cook_body")) +} +func (g *tableGen) cookExtentSignature(st *ir.Struct) string { + return fmt.Sprintf("static SCHEMA_UNUSED int %s(TableNumbering * n,const void * storage,uint8_t * record,uint8_t * extent,int64_t * at,int order)", g.sym(st.Name, "cook_extent")) +} +func (g *tableGen) emitCookWriteDeclarations(members []*ir.Struct) { + for _, st := range members { + g.pf("%s;\n", g.cookBodySignature(st)) + if g.hasCookExtent(st) { + g.pf("%s;\n", g.cookExtentSignature(st)) + } + } +} +func (g *tableGen) emitCookWriteSurface(members []*ir.Struct) { + for _, st := range members { + g.pf("%s\n{\n const %s * value=(const %s *)storage;\n (void)n; (void)at; (void)value; (void)order;\n", g.cookBodySignature(st), st.Name, st.Name) + step := 0 + for _, fl := range ir.RecordLayout(g.unit, st).Fields { + g.cookField(fl.Field, "value->"+fl.Field.Name, fmt.Sprintf("at+%d", fl.Offset), fl.Offset, " ", &step) + } + g.pf(" return 1;\n}\n") + if g.hasCookExtent(st) { + g.pf("%s\n{\n const %s * value=(const %s *)storage;\n (void)n; (void)value; (void)record; (void)extent; (void)at; (void)order;\n", g.cookExtentSignature(st), st.Name, st.Name) + step = 0 + for _, fl := range ir.RecordLayout(g.unit, st).Fields { + g.cookExtentField(fl.Field, "value->"+fl.Field.Name, fmt.Sprintf("(record!=NULL ? record+%d : NULL)", fl.Offset), " ", &step) + } + g.pf(" return 1;\n}\n") + } + if !st.IsTable || st.IsMapEntry() { + continue + } + if g.isVar(st.Name) { + g.emitCookVariableRoot(st) + } else { + g.emitCookFixedRoot(st) + } + } +} + +func (g *tableGen) cookField(f *ir.Field, src, at string, offset int64, ind string, step *int) { + if f.IsMap() || f.IsList() { + return + } // zero until the extent writer places the array + pieces := ir.FieldPieces(g.unit, f, offset) + if len(pieces) == 0 { + return + } + if f.Type.Optional { + g.pf("%stable_cook_put(%s+%d,%s_present ? 1 : 0,1,order);\n", ind, at, pieces[len(pieces)-1].Offset-offset, src) + } + if f.Array != ir.ArrayNone { + *step++ + i := fmt.Sprintf("cook_i%d", *step) + elem := sequenceElement(f) + g.pf("%s{ int32_t %s; for(%s=0;%s<%d;%s++) {\n", ind, i, i, i, f.ArrayBound, i) + g.cookField(elem, src+"["+i+"]", fmt.Sprintf("%s+%s*%d", at, i, pieces[0].Size/f.ArrayBound), 0, ind+" ", step) + g.pf("%s} }\n", ind) + if f.Array == ir.ArrayCounted { + g.pf("%stable_cook_put(%s+%d,(uint32_t)%s_count,4,order);\n", ind, at, pieces[1].Offset-offset, src) + } + return + } + if f.Type.Pointer { + g.pf("%sif(!table_cook_ref(n,%s,&%s,order)) { return 0; }\n", ind, at, src) + return + } + t := f.Type + switch t.Kind { + case ir.TString, ir.TBytes, ir.TWString: + if t.Kind == ir.TWString { + g.pf("%stable_cook_units(%s,%s,%s_length,%d,order);\n", ind, at, src, src, t.Size+1) + } else { + g.pf("%stable_cook_bytes(%s,%s,%s_length,%d);\n", ind, at, src, src, pieces[0].Size) + } + g.pf("%stable_cook_put(%s+%d,(uint32_t)%s_length,4,order);\n", ind, at, pieces[1].Offset-offset, src) + case ir.TBool: + g.pf("%stable_cook_put(%s,%s ? 1 : 0,1,order);\n", ind, at, src) + case ir.TFloat32, ir.TFloat64: + width := 32 + if t.Kind == ir.TFloat64 { + width = 64 + } + g.pf("%s{ uint%d_t bits; memcpy(&bits,&%s,%d); table_cook_put(%s,bits,%d,order); }\n", ind, width, src, width/8, at, width/8) + case ir.TInt, ir.TFixed: + if t.Width == 128 { + g.pf("%stable_cook_put128(%s,%s.lo,%s.hi,order);\n", ind, at, src, src) + } else { + g.pf("%stable_cook_put(%s,(uint64_t)%s,%d,order);\n", ind, at, src, t.Width/8) + } + case ir.TBits: + width := 8 + if t.Width <= 32 { + width = 4 + } + g.pf("%stable_cook_put(%s,(uint64_t)%s,%d,order);\n", ind, at, src, width) + case ir.TNamed: + switch ref := t.Ref.(type) { + case *ir.Enum: + g.pf("%stable_cook_put(%s,(uint64_t)%s,%d,order);\n", ind, at, src, ir.StorageBitsFor(ref.Max)/8) + case *ir.Flags: + g.pf("%stable_cook_put(%s,(uint64_t)%s,8,order);\n", ind, at, src) + case *ir.Struct: + g.pf("%sif(!%s(n,%s,&%s,order)) { return 0; }\n", ind, g.sym(ref.Name, "cook_body"), at, src) + case *ir.Union: + _, _, tag, offset := ir.UnionLayout(g.unit, ref) + g.pf("%stable_cook_put(%s,(uint64_t)%s.type,%d,order);\n%sswitch(%s.type) {\n", ind, at, src, tag, ind, src) + for _, v := range ref.Variants { + if !v.Void() { + g.pf("%scase %s: {\n", ind, enumConst(ref.Name+"Type", v.Name)) + g.cookField(v.F, armValue(src, v), fmt.Sprintf("%s+%d", at, offset), offset, ind+" ", step) + g.pf("%s break; }\n", ind) + } + } + g.pf("%sdefault: break;\n%s}\n", ind, ind) + } + } +} + +func (g *tableGen) cookExtentField(f *ir.Field, src, dst, ind string, step *int) { + if !g.cookFieldExtent(f) { + return + } + *step++ + label := fmt.Sprintf("cook_extent%d", *step) + g.pf("%suint8_t * %s_slot=%s;\n", ind, label, dst) + dst = label + "_slot" + switch { + case f.IsList() || f.IsMap(): + typ := g.sequenceType(f) + elem := sequenceElement(f) + g.pf("%s{ TableSequenceCursor %s=%s; int32_t %s_i; uint8_t * %s_array;\n", ind, label, g.sequenceCursor(f, "n", src), label, label) + g.pf("%s if(!%s.ok || !table_extent_reserve(at,%s.count,sizeof(%s),SCHEMA_TABLE_ALIGNOF(%s))) { return 0; }\n", ind, label, src, typ, typ) + g.pf("%s %s_array=%s!=NULL ? extent+*at-(int64_t)%s.count*sizeof(%s) : NULL;\n", ind, label, dst, src, typ) + g.pf("%s if(%s_array!=NULL) { table_cook_put(%s,%s.count ? (uint64_t)(%s_array-(%s)) : 0,8,order); table_cook_put(%s+8,(uint32_t)%s.count,4,order); }\n", ind, label, dst, src, label, dst, dst, src) + g.pf("%s for(%s_i=0;%s_i<%s.count;%s_i++) {\n", ind, label, label, src, label) + g.pf("%s const %s * %s_in=(const %s *)table_sequence_next(&%s); if(%s_in==NULL) { return 0; }\n", ind, typ, label, typ, label, label) + out := fmt.Sprintf("(%s_array!=NULL ? %s_array+%s_i*sizeof(%s) : NULL)", label, label, label, typ) + g.pf("%s if(%s_array!=NULL) {\n", ind, label) + g.cookField(elem, "(*"+label+"_in)", out, 0, ind+" ", step) + g.pf("%s }\n", ind) + g.cookExtentField(elem, "(*"+label+"_in)", out, ind+" ", step) + g.pf("%s }\n%s}\n", ind, ind) + case f.Array != ir.ArrayNone: + count := fmt.Sprint(f.ArrayBound) + if f.Array == ir.ArrayCounted { + count = src + "_count" + } + elem := sequenceElement(f) + pieces := ir.FieldPieces(g.unit, f, 0) + stride := pieces[0].Size / f.ArrayBound + g.pf("%s{ int32_t %s; for(%s=0;%s<%s && %s<%d;%s++) {\n", ind, label, label, label, count, label, f.ArrayBound, label) + g.cookExtentField(elem, src+"["+label+"]", fmt.Sprintf("(%s!=NULL ? %s+%s*%d : NULL)", dst, dst, label, stride), ind+" ", step) + g.pf("%s}\n", ind) + if f.Array == ir.ArrayCounted { + // A hidden container has no place in the region. Measure that + // slot independently and refuse even when its shape is invalid. + g.pf("%sfor(;%s<%d;%s++) { int64_t %s_hidden=0; int64_t * %s_saved_at=at; at=&%s_hidden;\n", ind, label, f.ArrayBound, label, label, label, label) + g.cookExtentField(elem, src+"["+label+"]", "((uint8_t *)NULL)", ind+" ", step) + g.pf("%s at=%s_saved_at; if(%s_hidden!=0) { return 0; }\n%s}\n", ind, label, label, ind) + } + g.pf("%s}\n", ind) + default: + switch ref := f.Type.Ref.(type) { + case *ir.Struct: + g.pf("%sif(!%s(n,&%s,%s,extent,at,order)) { return 0; }\n", ind, g.sym(ref.Name, "cook_extent"), src, dst) + case *ir.Union: + _, _, _, offset := ir.UnionLayout(g.unit, ref) + g.pf("%sswitch(%s.type) {\n", ind, src) + for _, v := range ref.Variants { + if !v.Void() && g.cookFieldExtent(v.F) { + g.pf("%scase %s: {\n", ind, enumConst(ref.Name+"Type", v.Name)) + g.cookExtentField(v.F, armValue(src, v), fmt.Sprintf("(%s!=NULL ? %s+%d : NULL)", dst, dst, offset), ind+" ", step) + g.pf("%s break; }\n", ind) + } + } + g.pf("%sdefault: break;\n%s}\n", ind, ind) + } + } +} + +func (g *tableGen) emitCookFixedRoot(st *ir.Struct) { + ml := ir.RecordLayout(g.unit, st) + align := ir.RegionAlignOf(ml.Align) + bytes := cookAlignUp(ml.Size, align) + need := 64 + bytes + 16 + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * value) { (void)value; return %d; }\n", g.api(st.Name, "cook_measure"), st.Name, need) + g.pf("static SCHEMA_UNUSED int %s(const %s * value,void * output,uint64_t capacity,TableByteOrder order)\n{\n uint8_t * raw=(uint8_t *)output;\n if(value==NULL || raw==NULL || capacity<%d) { return 0; }\n memset(raw,0,%d);\n if(!%s(NULL,raw+64,value,order)) { return 0; }\n", g.api(st.Name, "cook"), st.Name, need, need, g.sym(st.Name, "cook_body")) + g.pf(" table_cook_put(raw+%d,UINT64_C(0x%016x),8,order);\n table_cook_header(raw,%s,%d,1,%d,order); return 1;\n}\n", 64+bytes+8, ir.TableWireId(st.WireName()), buildVersionName(g.unit.Package), bytes, align) +} +func (g *tableGen) emitCookVariableRoot(st *ir.Struct) { + g.pf("static SCHEMA_UNUSED int64_t %s(const TableCtx * ctx,const %s * value,TableAllocator allocator)\n{ return table_graph_cook(ctx,value,&%s,NULL,0,TableByteOrder_Little,allocator,%s,1); }\n", g.api(st.Name, "cook_measure_with_allocator"), st.Name, g.graphType(st.Name), buildVersionName(g.unit.Package)) + g.pf("static SCHEMA_UNUSED int %s(const TableCtx * ctx,const %s * value,void * output,uint64_t capacity,TableByteOrder order,TableAllocator allocator)\n{ return table_graph_cook(ctx,value,&%s,output,capacity,order,allocator,%s,0)>=0; }\n", g.api(st.Name, "cook_with_allocator"), st.Name, g.graphType(st.Name), buildVersionName(g.unit.Package)) + alloc := "ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()" + g.pf("static SCHEMA_UNUSED int64_t %s(const TableCtx * ctx,const %s * value)\n{ return %s(ctx,value,%s); }\n", g.api(st.Name, "cook_measure"), st.Name, g.api(st.Name, "cook_measure_with_allocator"), alloc) + g.pf("static SCHEMA_UNUSED int %s(const TableCtx * ctx,const %s * value,void * output,uint64_t capacity,TableByteOrder order)\n{ return %s(ctx,value,output,capacity,order,%s); }\n", g.api(st.Name, "cook"), st.Name, g.api(st.Name, "cook_with_allocator"), alloc) +} diff --git a/internal/codegen/ctable/ctable.go b/internal/codegen/ctable/ctable.go index 04347a273..825340b5c 100644 --- a/internal/codegen/ctable/ctable.go +++ b/internal/codegen/ctable/ctable.go @@ -63,10 +63,12 @@ package ctable import ( "fmt" + "maps" "sort" "strconv" "strings" + "github.com/mas-bandwidth/schema/v2/internal/codegen/ccommon" "github.com/mas-bandwidth/schema/v2/ir" ) @@ -97,28 +99,24 @@ const ( func tableScalarKind(f *ir.Field) int { return ir.TableScalarKind(f) } -func tableKindWidth(kind int) int { - switch kind { - case tkBool, tkI8, tkU8: - return 1 - case tkI16, tkU16: - return 2 - case tkI32, tkU32, tkF32: - return 4 - case tkI64, tkU64, tkF64: - return 8 - } - return 0 -} +func tableKindWidth(kind int) int { return ir.TableKindWidth(kind) } func tablePut(width int) string { return fmt.Sprintf("table_writer_put%d", width*8) } func tableGet(width int) string { return fmt.Sprintf("table_reader_get%d", width*8) } type tableGen struct { - unit *ir.Unit - file *ir.File - anyVariable bool // the unit declares at least one variable-length table - anyKeyed bool // the unit declares at least one enum-keyed array + retain bool // emit the opt-in parallel body family + + messageSlots map[string]uint64 + descriptorUnions map[string]bool + outside bool // descriptors outside the checked table closure carry no wire ids + unit *ir.Unit + file *ir.File + anyVariable bool // the unit declares at least one variable-length table + anySequence bool + anyList bool + anyMap bool + anyKeyed bool // the unit declares at least one enum-keyed array // blocks is the unit's BLOCK FORM surface (docs/SPEC-TABLES.md §19), nil when // no table is marked `| block`. Nil is what makes the zero-cost gate // answerable by asking one question (§2.2). @@ -146,8 +144,18 @@ func (g *tableGen) pf(format string, args ...any) { g.body.WriteString(s) } +func (g *tableGen) declBase(name string) string { + if base := g.unit.DeclFile[name]; base != "" { + return base + } + if st := g.unit.Tables[name]; st != nil && st.MapEntryOf != "" { + owner, _, _ := strings.Cut(st.MapEntryOf, ".") + return g.declBase(owner) + } + return "" +} func (g *tableGen) noteRef(name string) { - if base, ok := g.unit.DeclFile[name]; ok && base != g.file.Base { + if base := g.declBase(name); base != "" && base != g.file.Base { g.includes[base] = true } } @@ -195,9 +203,9 @@ func unitHasKeyedArray(u *ir.Unit, closure map[string]bool) bool { // unit that declares one (docs/SPEC-TABLES.md §2.4). // // C's storage IS the array — `T slots[E_MAX]`, the key k at index k-1 — so -// there is no wrapper type to emit and no size parameter to spell: the extent -// comes from the key enum's own _MAX and from nowhere else. What C++ puts in -// operator[] lives here instead. +// there is no wrapper type to emit. The caller passes the key enum's own _MAX +// explicitly, including when the array has decayed to a pointer. What C++ puts +// in operator[] lives here instead. // // NONE IS THE NULL KEY: it names no slot, it never rides on the wire, a stored // key of 0 is malformed, and INDEXING BY IT IS A PROGRAM ERROR IN EVERY @@ -206,31 +214,41 @@ func unitHasKeyedArray(u *ir.Unit, closure map[string]bool) bool { // debugger can read it and NDEBUG removes that; the abort is what stands after // it. The cost is one perfectly-predicted compare, on a path that reads config. const tableKeyedAccessor = ` -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) ` // tablePrimitives is the shared runtime, emitted into every Table.h behind a // per-package guard — one definition per TU whatever the include order, and a // lone Table.h works standalone. -func tablePrimitives(pkg string, anyVariable bool, anyKeyed bool) string { +func tablePrimitives(pkg string, anyVariable bool, anyKeyed bool, wireRuntime string) string { + idType := "uint64_t" + if wireRuntime == "" { + idType = "uint16_t" + wireRuntime = legacyTableWireRuntime(tableInlineMacro(pkg)) + } keyed := "" if anyKeyed { keyed = tableKeyedAccessor @@ -327,8 +345,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -337,6 +361,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -346,6 +371,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -372,7 +399,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + ` + idType + ` id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -382,12 +409,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + ` + idType + ` id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */` + pointerFieldMember + ` int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -401,6 +430,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -428,6 +459,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -449,7 +482,20 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; -typedef struct TableWriter +` + wireRuntime + keyed + ` +static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } +static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } +static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } +static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } + +#endif /* ` + guard + ` */ +` +} + +// legacyTableWireRuntime remains with the variable carrier until its node-table +// codec is ported. Fixed units select the form-1 runtime in wire.go. +func legacyTableWireRuntime(forceInline string) string { + return `typedef struct TableWriter { uint8_t * buffer; int64_t capacity; @@ -581,13 +627,6 @@ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) } return 0; } -` + keyed + ` -static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } -static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } -static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } -static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } - -#endif /* ` + guard + ` */ ` } @@ -596,10 +635,7 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem // generated tree is byte-identical with or without this package. func Generate(u *ir.Unit) (map[string][]byte, error) { if len(u.Tables) == 0 { - return map[string][]byte{}, nil - } - if err := ir.RefuseWideTableKinds(u, "C"); err != nil { - return nil, err + return generateViewFiles(u, nil, nil, nil, false, false, false), nil } bases := map[string]bool{} for _, f := range u.Files { @@ -615,6 +651,20 @@ func Generate(u *ir.Unit) (map[string][]byte, error) { targets := ir.PointerTargets(u) anyVariable := len(variable) > 0 anyKeyed := unitHasKeyedArray(u, closure) + anyList, anyMap := false, false + for name := range closure { + st := u.Tables[name] + if st == nil { + st = u.Structs[name] + } + if st == nil { + continue + } + for _, field := range st.Fields { + anyList = anyList || field.IsList() + anyMap = anyMap || field.IsMap() + } + } blocks := ir.Blocks(u) // The BLOCK FORM (docs/SPEC-TABLES.md §19) is emitted ON THE SIDE, into @@ -623,7 +673,7 @@ func Generate(u *ir.Unit) (map[string][]byte, error) { // the form. The Table header below carries not one symbol of it. out := generateBlockFiles(u, blocks, variable, targets) for _, f := range u.Files { - g := &tableGen{unit: u, file: f, anyVariable: anyVariable, anyKeyed: anyKeyed, blocks: blocks, variable: variable, targets: targets, + g := &tableGen{unit: u, file: f, anyVariable: anyVariable, anyKeyed: anyKeyed, anySequence: anyList || anyMap, anyList: anyList, anyMap: anyMap, blocks: blocks, variable: variable, targets: targets, includes: map[string]bool{}} var members []*ir.Struct members = append(members, orderTables(f.Tables)...) @@ -632,28 +682,79 @@ func Generate(u *ir.Unit) (map[string][]byte, error) { members = append(members, st) } } - cg := &tableGen{unit: u, file: f, anyVariable: anyVariable, anyKeyed: anyKeyed, blocks: blocks, variable: variable, targets: targets, + cg := &tableGen{unit: u, file: f, anyVariable: anyVariable, anyKeyed: anyKeyed, anySequence: anyList || anyMap, anyList: anyList, anyMap: anyMap, blocks: blocks, variable: variable, targets: targets, includes: map[string]bool{}} + g.emitTableShapes(members) if len(members) > 0 { - for _, st := range members { - if st.IsTable { - g.emitTableStruct(st) - } - } g.emitTableResetDeclarations(members) for _, st := range members { g.emitTableReset(st) } + if g.anyMap { + for _, st := range members { + for _, field := range st.Fields { + if field.IsMap() { + g.emitMapHelpers(st, field) + } + } + } + } g.emitCodecDeclarations(members) + g.emitCookWriteDeclarations(members) + if g.anyVariable && g.fileWire() { + g.emitGraphDeclarations(members) + g.emitExtentDeclarations(members) + } + unions := g.fileWireUnions() + g.emitMessageSaveDeclarations(members, unions) + g.emitMessageReadDeclarations(members, unions) + g.emitMessageExtentDeclarations(members, unions) + for _, un := range unions { + g.emitMessageUnionSave(un) + } + for _, un := range unions { + g.emitMessageUnionRead(un) + } + for _, un := range unions { + g.emitMessageUnionExtent(un) + } + if g.fileWire() { + g.emitUnionWireDeclarations(unions) + for _, un := range unions { + g.emitUnionWire(un) + } + } for _, st := range members { g.owner = st - g.emitTableMeasure(st) - g.emitTableWrite(st) - g.emitTableSave(st) - g.emitTableRead(st) + g.emitKeyedAccessors(st) + if g.fileWire() { + g.emitWireWrite(st) + g.emitWireRead(st) + g.emitMessageSave(st) + g.emitMessageRead(st) + g.emitMessageExtent(st) + } else { + g.emitTableMeasure(st) + g.emitTableWrite(st) + g.emitTableSave(st) + g.emitTableRead(st) + } } + if g.anySequence { + for _, st := range members { + for _, field := range st.Fields { + if field.IsList() { + g.emitListSurface(st, field) + } + } + } + } + g.emitRetainBodies(members, unions) g.emitVariableSurface(members) + g.emitRetainRoots(members) + g.emitRetainRefusals(members) g.emitCookSurface(members) + g.emitCookWriteSurface(members) g.emitRelocatabilityPreamble() g.emitCookLayoutAsserts(members) g.pf("/* ---- reflection descriptors (tables only, docs/SPEC-TABLES.md) ----\n\n") @@ -685,6 +786,25 @@ func Generate(u *ir.Unit) (map[string][]byte, error) { cg.emitJsonDefinitions(st) } } else { + if unions := g.fileWireUnions(); g.fileWire() && len(unions) > 0 { + g.emitMessageSaveDeclarations(nil, unions) + g.emitMessageReadDeclarations(nil, unions) + g.emitMessageExtentDeclarations(nil, unions) + for _, un := range unions { + g.emitMessageUnionSave(un) + } + for _, un := range unions { + g.emitMessageUnionRead(un) + } + for _, un := range unions { + g.emitMessageUnionExtent(un) + } + g.emitUnionWireDeclarations(unions) + for _, un := range unions { + g.emitUnionWire(un) + } + } + g.emitRetainBodies(nil, g.fileWireUnions()) g.pf("/* no tables declared or referenced in this file — codecs are emitted\n") g.pf(" for the table closure only (`table` declarations and what they reach) */\n") } @@ -693,6 +813,10 @@ func Generate(u *ir.Unit) (map[string][]byte, error) { out[f.Base+"Table.c"] = tableSource(u, f, g, cg, members) } } + maps.Copy(out, generateViewFiles(u, closure, variable, targets, anyKeyed, anyList, anyMap)) + for name, data := range out { + out[name] = []byte(formatCompactCFunctions(string(data))) + } return out, nil } @@ -702,27 +826,24 @@ func (g *tableGen) header(u *ir.Unit, f *ir.File, members []*ir.Struct) []byte { guard := "SCHEMA_" + strings.ToUpper(u.Package) + "_" + strings.ToUpper(f.Base) + "TABLE_H" fmt.Fprintf(&h, "/* Code generated by the schema compiler from %s.schema. DO NOT EDIT.\n SPDX-License-Identifier: NONE — this generated output is yours, under terms of\n your choice. See the LICENSE exception in the schema compiler; the compiler is\n AGPL-3.0, its output is not.\n", f.Base) fmt.Fprintf(&h, " package %s — protocol id 0x%016x (packets only: tables version by field id, not by protocol id)\n", u.Package, u.ProtocolId) - h.WriteString(" The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize\n") - h.WriteString(" dependency — includable from any TU. Compile the .c beside this header\n") - h.WriteString(" to use the reflection descriptors or the text form. */\n\n") + h.WriteString(" The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c\n") + h.WriteString(" beside this header to use descriptors or JSON. 128-bit storage uses\n") + h.WriteString(" the lane types from serialize.h. */\n\n") fmt.Fprintf(&h, "#ifndef %s\n#define %s\n\n", guard, guard) h.WriteString("#include \n#include /* offsetof, for the reflection descriptors */\n#include /* memcpy, memset — the prefill and the wire's raw moves */\n") - if g.anyVariable { - // VARIABLE-LENGTH tables only: a unit of pointer-free tables pays - // for none of these headers (docs/SPEC-TABLES.md §2, the zero-cost gate) - h.WriteString("#include /* the arena's segments (the AUTHORING path may allocate) */\n") - } if g.anyKeyed { // ENUM-KEYED arrays only: indexing one by None is a program error in // EVERY configuration, and the accessor is where a runtime key can // first be caught (docs/SPEC-TABLES.md §2.4). The refusal aborts, so it // needs whether or not NDEBUG keeps the assert. h.WriteString("#include /* the keyed accessor's None refusal, in a debug build */\n") - if !g.anyVariable { - h.WriteString("#include /* and its abort, which NDEBUG does not remove */\n") - } + h.WriteString("#include /* and its abort, which NDEBUG does not remove */\n") } fmt.Fprintf(&h, "\n#include \"%s.h\"\n", f.Base) + if unitHasWideStorage(u) { + h.WriteString(ccommon.Align16) + h.WriteString("#include \"serialize.h\" /* 128-bit storage, shared with the packet header */\n") + } names := make([]string, 0, len(g.includes)) for n := range g.includes { names = append(names, n) @@ -738,10 +859,25 @@ func (g *tableGen) header(u *ir.Unit, f *ir.File, members []*ir.Struct) []byte { // defines it and the other agrees. h.WriteString("\n#ifndef SCHEMA_UNUSED\n#if defined(__GNUC__) || defined(__clang__)\n#define SCHEMA_UNUSED __attribute__((unused))\n#else\n#define SCHEMA_UNUSED\n#endif\n#endif\n") h.WriteString("\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n") - h.WriteString(tablePrimitives(u.Package, g.anyVariable, g.anyKeyed)) + h.WriteString(g.wirePrimitives()) + h.WriteString(tableRefuseRuntime) if g.anyVariable { h.WriteString("\n") + h.WriteString(tableAllocatorRuntime) h.WriteString(tableArenaRuntime(u.Package)) + if g.fileWire() { + h.WriteString(tableBlobRuntime()) + if g.anySequence { + h.WriteString(tableSequenceRuntime) + } + if g.anyList { + h.WriteString("#ifndef SCHEMA_TABLE_LIST_RUNTIME\n#define SCHEMA_TABLE_LIST_RUNTIME\ntypedef TableSequence TableList;\n#endif\n") + } + if g.anyMap { + h.WriteString(tableMapRuntime) + } + h.WriteString(strings.Replace(tableGraphRuntime, "@BLOB_GRAPH@", tableBlobGraphRuntime, 1)) + } } // the COOKED FORM's read side (docs/SPEC-TABLES.md §7) and the BUILD VERSION // it matches against, in EVERY unit that declares a table: every table @@ -751,6 +887,12 @@ func (g *tableGen) header(u *ir.Unit, f *ir.File, members []*ir.Struct) []byte { h.WriteString(buildVersionConstant(u.Package, ir.BuildVersion(u))) h.WriteString("\n") h.WriteString(tableCookRuntime(u.Package)) + h.WriteString(tableCookWriteRuntime) + h.WriteString(tableMessageForm(u, g.anyVariable)) + if g.anyVariable { + h.WriteString(tableCookGraphRuntime) + h.WriteString(g.retainRuntime()) + } h.WriteString("\n") h.WriteString(g.body.String()) fmt.Fprintf(&h, "\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* %s */\n", guard) @@ -770,15 +912,12 @@ func tableSource(u *ir.Unit, f *ir.File, g *tableGen, cg *tableGen, members []*i c.WriteString("#include /* the text form: number formatting */\n") c.WriteString("#include /* the text form: exact number conversion */\n") c.WriteString("#include /* the text form: the runtime's decimal point */\n\n") - fixed := 0 - for _, st := range members { - if !g.isVar(st.Name) { - fixed++ - } - } - if fixed > 0 { + if len(members) > 0 { c.WriteString(tableJsonWalk(u.Package)) c.WriteString("\n") + if g.anyVariable { + c.WriteString(tableJsonGraphSource) + } } c.WriteString(cg.body.String()) return []byte(c.String()) diff --git a/internal/codegen/ctable/extent.go b/internal/codegen/ctable/extent.go new file mode 100644 index 000000000..b734580e3 --- /dev/null +++ b/internal/codegen/ctable/extent.go @@ -0,0 +1,209 @@ +package ctable + +import ( + "fmt" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +func (g *tableGen) emitExtentDeclarations(members []*ir.Struct) { + for _, st := range g.varMembers(members) { + g.pf("static SCHEMA_UNUSED int %s(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at);\n", g.sym(st.Name, "extent")) + g.pf("static SCHEMA_UNUSED int %s(TableReader r,int64_t * at);\n", g.sym(st.Name, "wire_extent")) + g.pf("static SCHEMA_UNUSED int64_t %s(const TableReader * r);\n", g.sym(st.Name, "wire_storage")) + } + for _, un := range g.fileWireUnions() { + g.pf("static SCHEMA_UNUSED int %s(TableReader * r,int64_t * at);\n", g.sym(un.Name, "wire_extent")) + } +} + +func (g *tableGen) emitExtentBodies(members []*ir.Struct) { + for _, un := range g.fileWireUnions() { + g.pf("static SCHEMA_UNUSED int %s(TableReader * r,int64_t * at)\n{\n uint64_t ref,id; TableReader body;\n (void)at;\n", g.sym(un.Name, "wire_extent")) + g.pf(" if(!table_reader_leb(r,&ref)) { r->offset=r->size; return 1; } if(ref==0) { return 1; }\n if(ref>r->id_count || !table_reader_has(r,1)) { r->offset=r->size; return 1; } id=table_reader_id_at(r,ref); r->offset++;\n if(!table_reader_span(r,&body)) { r->offset=r->size; return 1; }\n switch(id) {\n") + for _, v := range un.Variants { + if !v.Void() { + g.pf(" case UINT64_C(0x%016x): {\n", ir.TableWireId(v.WireName())) + step := 0 + g.emitWireExtentField(v.F, "body", true, " ", &step) + g.pf(" } break;\n") + } + } + g.pf(" default: break;\n } return 1;\n}\n") + } + for _, st := range g.varMembers(members) { + g.pf("static SCHEMA_UNUSED int %s(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at)\n{\n const %s * value=(const %s *)storage; %s * out=(%s *)(void *)target;\n (void)n; (void)value; (void)out; (void)at;\n", g.sym(st.Name, "extent"), st.Name, st.Name, st.Name, st.Name) + step := 0 + guards := tableGuardExprs(st) + for _, f := range st.Fields { + if guard := guards[f.Name]; guard != "" { + g.pf(" if(%s) {\n", guard) + } + g.emitExtentField(f, "value->"+f.Name, "out->"+f.Name, " ", &step) + if guards[f.Name] != "" { + g.pf(" }\n") + } + } + g.pf(" return 1;\n}\n") + g.pf("static SCHEMA_UNUSED int %s(TableReader r,int64_t * at)\n{\n (void)at;\n for(;;) { uint64_t ref,id; uint8_t kind;\n if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; }\n id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r);\n switch(id) {\n", g.sym(st.Name, "wire_extent")) + step = 0 + for _, f := range st.Fields { + if !g.fieldHasExtent(f) { + continue + } + kind := ir.TableWireScalarKind(f) + if f.Array != ir.ArrayNone || f.IsMap() { + kind = tkArray + } + if f.KeyEnum != "" { + kind = tkKeyed + } + g.pf(" case UINT64_C(0x%016x): if(kind==%d) {\n", ir.TableFieldWireId(f), kind) + g.emitWireExtentField(f, "r", false, " ", &step) + g.pf(" continue; } break;\n") + } + g.pf(" default: break;\n }\n if(!table_reader_skip(&r,kind)) { return 1; }\n }\n}\n") + g.pf("static SCHEMA_UNUSED int64_t %s(const TableReader * r)\n{\n int64_t at=sizeof(%s); if(!%s(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at);\n}\n", g.sym(st.Name, "wire_storage"), st.Name, g.sym(st.Name, "wire_extent")) + } +} + +func (g *tableGen) fieldHasExtent(f *ir.Field) bool { + if f.IsMap() || f.IsList() { + return true + } + if f.Type.Pointer { + return false + } + switch ref := f.Type.Ref.(type) { + case *ir.Struct: + return g.isVar(ref.Name) + case *ir.Union: + for _, v := range ref.Variants { + if !v.Void() && g.fieldHasExtent(v.F) { + return true + } + } + } + return false +} + +func (g *tableGen) emitExtentField(f *ir.Field, src, dst, ind string, step *int) { + if f.Type.Optional { + g.pf("%sif(%s_present) {\n", ind, src) + } + switch { + case f.IsList() || f.IsMap(): + *step++ + label := fmt.Sprintf("extent%d", *step) + typ := g.sequenceType(f) + elem := sequenceElement(f) + g.pf("%s{ TableSequenceCursor %s=%s; int32_t %s_i; uint8_t * %s_array=NULL;\n", ind, label, g.sequenceCursor(f, "n", src), label, label) + g.pf("%s if(!%s.ok || !table_extent_reserve(at,%s.count,sizeof(%s),SCHEMA_TABLE_ALIGNOF(%s))) { return 0; }\n", ind, label, src, typ, typ) + g.pf("%s if(target!=NULL) { %s_array=n->pack_base+*at-(int64_t)%s.count*sizeof(%s); %s.items.value=%s.count ? (int64_t)(%s_array-(uint8_t *)(void *)&%s.items) : 0; }\n", ind, label, src, typ, dst, src, label, dst) + g.pf("%s for(%s_i=0;%s_i<%s.count;%s_i++) {\n", ind, label, label, src, label) + g.pf("%s const %s * %s_in=(const %s *)table_sequence_next(&%s); %s * %s_out=target ? (%s *)(void *)%s_array+%s_i : NULL;\n", ind, typ, label, typ, label, typ, label, typ, label, label) + g.pf("%s if(%s_in==NULL) { return 0; } if(%s_out!=NULL) { memcpy(%s_out,%s_in,sizeof(%s)); }\n", ind, label, label, label, label, typ) + g.emitExtentField(elem, "(*"+label+"_in)", "(*"+label+"_out)", ind+" ", step) + g.pf("%s }\n%s}\n", ind, ind) + case f.Array != ir.ArrayNone: + *step++ + index := fmt.Sprintf("extent_i%d", *step) + count := fmt.Sprint(f.ArrayBound) + if f.Array == ir.ArrayCounted { + count = src + "_count" + } + g.pf("%s{ int32_t %s; for(%s=0;%s<%s;%s++) {\n", ind, index, index, index, count, index) + e := *f + e.Array = ir.ArrayNone + e.Type.Optional = false + g.emitExtentField(&e, src+"["+index+"]", dst+"["+index+"]", ind+" ", step) + g.pf("%s} }\n", ind) + case f.Type.Pointer: + g.pf("%sif(target!=NULL) { const void * node=table_ref_at(n->ctx,&%s); uint64_t index=table_number_find(n,node);\n", ind, src) + g.pf("%s if(node!=NULL && index==0) { return 0; } %s.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&%s) : 0; }\n", ind, dst, dst) + default: + switch ref := f.Type.Ref.(type) { + case *ir.Struct: + if g.needsWalkers(ref.Name) { + g.pf("%sif(!%s(n,&%s,target ? (uint8_t *)(void *)&%s : NULL,at)) { return 0; }\n", ind, g.sym(ref.Name, "extent"), src, dst) + } + case *ir.Union: + g.pf("%sswitch(%s.type) {\n", ind, src) + for _, v := range ref.Variants { + if !v.Void() { + g.pf("%scase %s: {\n", ind, enumConst(ref.Name+"Type", v.Name)) + g.emitExtentField(v.F, armValue(src, v), armValue(dst, v), ind+" ", step) + g.pf("%s break; }\n", ind) + } + } + g.pf("%sdefault: break;\n%s}\n", ind, ind) + } + } + if f.Type.Optional { + g.pf("%s}\n", ind) + } +} + +// Only framing contributes to LoadMeasure. Damage stops a local scan; a count +// that cannot fit its storage or enclosing L refuses before any allocation. +func (g *tableGen) emitWireExtentField(f *ir.Field, r string, bounded bool, ind string, step *int) { + if !g.fieldHasExtent(f) { + return + } + if !f.IsList() && !f.IsMap() && f.Array == ir.ArrayNone && !f.Type.Pointer { + if un, ok := f.Type.Ref.(*ir.Union); ok { + g.pf("%sif(!%s(&%s,at)) { return 0; }\n", ind, g.sym(un.Name, "wire_extent"), r) + return + } + } + *step++ + b := fmt.Sprintf("extent_body%d", *step) + g.pf("%s{ TableReader %s;\n", ind, b) + if bounded { + g.pf("%s %s=%s;\n", ind, b, r) + } else { + g.pf("%s if(!table_reader_span(&%s,&%s)) { return 1; }\n", ind, r, b) + } + if f.Array != ir.ArrayNone || f.IsMap() { + e := sequenceElement(f) + kind := ir.TableWireScalarKind(e) + done := b + "_done" + g.pf("%s if(%s.size>=2) { uint64_t %s_count;\n", ind, b, b) + g.pf("%s uint8_t wire_kind=table_reader_get8(&%s);\n", ind, b) + g.pf("%s if((wire_kind!=%d && !(%s)) || !table_reader_leb(&%s,&%s_count)) { goto %s; }\n", ind, kind, wireElementWidenTest(f, kind, "wire_kind"), b, b, done) + if f.IsList() || f.IsMap() { + typ := g.sequenceType(f) + g.pf("%s int64_t floor=%d;\n", ind, sequenceFloor(f)) + if ir.TableKindSigned(kind) || kind >= tkU8 && kind <= tkU64 || kind == tkF64 { + for source := 1; source <= 29; source++ { + if ir.TableKindWidens(source, kind) { + g.pf("%s if(wire_kind==%d)floor=%d;\n", ind, source, tableKindWidth(source)) + } + } + } + g.pf("%s if(%s_count>INT32_MAX) { %s.report->reason=11; return 0; }\n", ind, b, b) + g.pf("%s if(%s_count>(uint64_t)((%s.size-%s.offset)/floor)) { %s.report->reason=10; return 0; }\n", ind, b, b, b, b) + g.pf("%s if(!table_extent_reserve(at,(int64_t)%s_count,sizeof(%s),SCHEMA_TABLE_ALIGNOF(%s))) { return 0; }\n", ind, b, typ, typ) + } + if g.fieldHasExtent(e) { + g.pf("%s { uint64_t i; for(i=0;i<%s_count;i++) {\n", ind, b) + g.pf("%s if(!table_reader_has(&%s,1)) { goto %s; }\n", ind, b, done) + if f.KeyEnum != "" { + g.pf("%s uint64_t key; if(!table_reader_leb(&%s,&key)) { goto %s; }\n", ind, b, done) + } + switch ref := e.Type.Ref.(type) { + case *ir.Struct: + g.pf("%s TableReader item; if(!table_reader_span(&%s,&item)) { goto %s; }\n", ind, b, done) + g.pf("%s if(!%s(item,at)) { return 0; }\n", ind, g.sym(ref.Name, "wire_extent")) + case *ir.Union: + g.pf("%s if(!%s(&%s,at)) { return 0; }\n", ind, g.sym(ref.Name, "wire_extent"), b) + } + g.pf("%s } }\n", ind) + } + g.pf("%s }\n%s%s: ;\n", ind, ind, done) + + } else if ref, ok := f.Type.Ref.(*ir.Struct); ok && g.needsWalkers(ref.Name) { + g.pf("%s if(!%s(%s,at)) { return 0; }\n", ind, g.sym(ref.Name, "wire_extent"), b) + } + g.pf("%s}\n", ind) +} diff --git a/internal/codegen/ctable/format.go b/internal/codegen/ctable/format.go new file mode 100644 index 000000000..ce05dc499 --- /dev/null +++ b/internal/codegen/ctable/format.go @@ -0,0 +1,108 @@ +package ctable + +import ( + "regexp" + "strings" +) + +// separateCStatements keeps an unbraced guard and the following statement +// on different lines. GCC diagnoses compact `if (x) return; next();` lines +// as misleading indentation. Only whitespace between C tokens changes; +// literals, comments, for headers and preprocessor directives are preserved. +func separateCStatements(source string) string { + var out strings.Builder + paren, line := 0, 0 + for i := 0; i < len(source); { + if i == line { + first := i + for first < len(source) && (source[first] == ' ' || source[first] == '\t') { + first++ + } + if first < len(source) && source[first] == '#' { + end := first + for end < len(source) { + if source[end] == '\n' && (end == 0 || source[end-1] != '\\') { + end++ + break + } + end++ + } + out.WriteString(source[i:end]) + i, line = end, end + continue + } + } + if end := retainedLiteralEnd(source, i); end > i { + out.WriteString(source[i:end]) + if last := strings.LastIndexByte(source[i:end], '\n'); last >= 0 { + line = i + last + 1 + } + i = end + continue + } + ch := source[i] + out.WriteByte(ch) + switch ch { + case '(': + paren++ + case ')': + paren-- + case '\n': + line = i + 1 + case ';': + if paren == 0 { + next := i + 1 + for next < len(source) && (source[next] == ' ' || source[next] == '\t') { + next++ + } + if next < len(source) && source[next] != '\n' && source[next] != '\r' { + indent := line + for indent < len(source) && (source[indent] == ' ' || source[indent] == '\t') { + indent++ + } + out.WriteByte('\n') + out.WriteString(source[line:indent]) + i = next + continue + } + } + } + i++ + } + return out.String() +} + +// The compact message and retention families are the new emitters that need +// this layout. Keep established table and JSON source pins unchanged. +var compactCFunction = regexp.MustCompile(`(?m)^[ \t]*static[^\n;{}]*\b[A-Za-z_0-9]*(?:message|retain|announce)[A-Za-z_0-9]*[ \t]*\([^;{}]*\)[ \t\r\n]*\{`) + +func formatCompactCFunctions(source string) string { + var out strings.Builder + start := 0 + for start < len(source) { + match := compactCFunction.FindStringIndex(source[start:]) + if match == nil { + out.WriteString(source[start:]) + break + } + begin, at := start+match[0], start+match[1] + out.WriteString(source[start:begin]) + depth := 1 + for at < len(source) && depth > 0 { + if end := retainedLiteralEnd(source, at); end > at { + at = end + continue + } + if source[at] == '{' { + depth++ + } + if source[at] == '}' { + depth-- + } + at++ + } + out.WriteString(separateCStatements(source[begin:at])) + start = at + } + return out.String() +} diff --git a/internal/codegen/ctable/format_test.go b/internal/codegen/ctable/format_test.go new file mode 100644 index 000000000..1b3bf0d19 --- /dev/null +++ b/internal/codegen/ctable/format_test.go @@ -0,0 +1,28 @@ +package ctable + +import ( + "strings" + "testing" +) + +func TestSeparateCStatements(t *testing.T) { + source := `#define STEPS(x) do { x++; x++; } while (0) +#define MORE(x) do { x++; \ + x++; } while (0) +/* a ; comment */ +int f(int x) { + const char * text="literal;still literal"; if(x)return 1;return 0; + for(int i=0;i<3;i++)x++;x++; + if(x) x++; else x--; +} +` + got := separateCStatements(source) + for _, want := range []string{`#define STEPS(x) do { x++; x++; } while (0)`, "#define MORE(x) do { x++; \\\n x++; } while (0)", `"literal;still literal"`, `for(int i=0;i<3;i++)`, "if(x)return 1;\n return 0;", "x++;\n else x--;"} { + if !strings.Contains(got, want) { + t.Fatalf("missing %q in %s", want, got) + } + } + if separateCStatements(got) != got { + t.Fatal("formatting is not idempotent") + } +} diff --git a/internal/codegen/ctable/graph.go b/internal/codegen/ctable/graph.go new file mode 100644 index 000000000..d6558651f --- /dev/null +++ b/internal/codegen/ctable/graph.go @@ -0,0 +1,609 @@ +package ctable + +import ( + "fmt" + "github.com/mas-bandwidth/schema/v2/ir" + "strings" +) + +// The variable file form uses one numbering shared by the root and every +// record. The work stack makes pointer depth independent of the C call stack; +// generated edge callbacks recurse only through finite by-value schema edges. +const tableGraphRuntime = ` +#ifndef SCHEMA_TABLE_GRAPH_RUNTIME +#define SCHEMA_TABLE_GRAPH_RUNTIME +typedef struct TableNumbering TableNumbering; +typedef struct TableNodeType +{ + uint64_t id; + int64_t size; + int (*save)( TableWriter * w, const void * value ); + int (*edges)( TableNumbering * numbering, const void * value ); + int blob; /* 0 table, 1 bytes, 2 terminated UTF-8 */ + int (*extent)(TableNumbering *,const void *,uint8_t *,int64_t *); + int64_t (*wire_storage)(const TableReader *); + int64_t alignment; + int has_extent; + int (*cook_body)(struct TableNumbering *,uint8_t *,const void *,int); + int (*cook_extent)(TableNumbering *,const void *,uint8_t *,uint8_t *,int64_t *,int); +} TableNodeType; + +typedef struct TableNodeEntry +{ + const void * value; + const TableNodeType * type; + uint64_t packed_offset; + int open; +} TableNodeEntry; + +typedef struct TableNodeWork +{ + const void * value; + const TableNodeType * type; + uint64_t index; + const TableRef * slot; + int close; +} TableNodeWork; + +struct TableNumbering +{ + const TableCtx * ctx; + uint8_t * pack_base; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + TableSequenceOrder * orders; +#endif + TableAllocator allocator; + TableNodeEntry * entries; + uint64_t count, capacity; + uint64_t * slots; + uint64_t slot_capacity; + TableNodeWork * work; + uint64_t work_count, work_capacity; +}; + +static SCHEMA_UNUSED int64_t table_node_storage( TableNumbering * n, const TableNodeType * type, const void * value ) +{ + int64_t at=type->size; + if(type->blob) { return table_blob_storage(((const TableBlob *)value)->length,type->blob==2); } + if(type->has_extent) { + int64_t extent=0; + if(!type->cook_extent(n,value,NULL,NULL,&extent,1) || at>INT64_MAX-kTableAlign) { return -1; } + at=table_align_up64(at); if(extent>INT64_MAX-at) { return -1; } at+=extent; + } + if(at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int64_t table_node_wire_storage( const TableNodeType * type, const TableReader * body ) +{ + if(type->blob && body->size>UINT32_MAX) { + body->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return -1; + } + return type->blob ? table_blob_storage(body->size,type->blob==2) : type->wire_storage(body); +} +@BLOB_GRAPH@ + +static SCHEMA_UNUSED uint64_t table_number_hash( const void * value ) +{ + uint64_t x = (uint64_t)(uintptr_t)value; + x ^= x >> 33; x *= UINT64_C(0xff51afd7ed558ccd); + x ^= x >> 33; x *= UINT64_C(0xc4ceb9fe1a85ec53); + return x ^ (x >> 33); +} + +static SCHEMA_UNUSED void * table_number_grow( TableAllocator allocator, void * memory, uint64_t * capacity, size_t width ) +{ + uint64_t next = *capacity ? *capacity * 2 : 64; + void * grown; + if ( next < *capacity || next > INT64_MAX / width ) { return 0; } + grown = table_allocate(allocator,(int64_t)next*(int64_t)width); + if ( grown == NULL ) { return 0; } + if(memory!=NULL) { memcpy(grown,memory,(size_t)*capacity*width); } + table_release(allocator,memory); *capacity = next; return grown; +} + +static SCHEMA_UNUSED int table_number_rehash( TableNumbering * n ) +{ + uint64_t capacity = n->slot_capacity ? n->slot_capacity * 2 : 128; + uint64_t * slots; + uint64_t i; + if ( capacity < n->slot_capacity || capacity > INT64_MAX / sizeof(uint64_t) ) { return 0; } + slots = (uint64_t *)table_allocate(n->allocator,(int64_t)capacity*sizeof(uint64_t)); + if ( slots == NULL ) { return 0; } + for ( i = 0; i < n->count; i++ ) + { + uint64_t at = table_number_hash(n->entries[i].value) & (capacity-1); + while ( slots[at] ) { at = (at+1) & (capacity-1); } + slots[at] = i+1; + } + table_release(n->allocator,n->slots); n->slots = slots; n->slot_capacity = capacity; return 1; +} + +static SCHEMA_UNUSED uint64_t table_number_find( const TableNumbering * n, const void * value ) +{ + uint64_t at; + if ( value == NULL || n == NULL || n->slot_capacity == 0 ) { return 0; } + at = table_number_hash(value) & (n->slot_capacity-1); + while ( n->slots[at] ) + { + uint64_t index = n->slots[at]; + if ( n->entries[index-1].value == value ) { return index; } + at = (at+1) & (n->slot_capacity-1); + } + return 0; +} + +static SCHEMA_UNUSED int table_number_edge( TableNumbering * n, const void * value, const TableNodeType * type ) +{ + TableNodeWork entry; + if ( value == NULL ) { return 1; } + if ( n->work_count == n->work_capacity ) + { + void * grown = table_number_grow(n->allocator,n->work,&n->work_capacity,sizeof(TableNodeWork)); + if ( grown == NULL ) { return 0; } + n->work = (TableNodeWork *)grown; + } + entry.value = value; entry.type = type; entry.index = 0; entry.close = 0; entry.slot = NULL; + n->work[n->work_count++] = entry; return 1; +} + +static SCHEMA_UNUSED void table_number_dispose( TableNumbering * n ) +{ +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + table_sequence_orders_release(n->allocator,n->orders); +#endif + table_release(n->allocator,n->entries); table_release(n->allocator,n->slots); table_release(n->allocator,n->work); + memset(n,0,sizeof(*n)); +} + +static SCHEMA_UNUSED int table_number_build( TableNumbering * n, const TableCtx * ctx, + const void * root, const TableNodeType * type, TableAllocator allocator ) +{ + memset(n,0,sizeof(*n)); n->ctx = ctx; n->allocator=allocator; + if ( root == NULL || !table_number_edge(n,root,type) ) { return 0; } + while ( n->work_count ) + { + TableNodeWork visit = n->work[--n->work_count]; + uint64_t index, at, first, last; + TableNodeEntry entry; + if ( visit.close ) { n->entries[visit.index].open = 0; continue; } + index = table_number_find(n,visit.value); + if ( index ) + { + if ( n->entries[index-1].open || n->entries[index-1].type->id != visit.type->id ) { return 0; } + continue; + } + if ( n->slot_capacity == 0 || n->count >= n->slot_capacity/2 ) + { if ( !table_number_rehash(n) ) { return 0; } } + if ( n->count == n->capacity ) + { + void * grown = table_number_grow(n->allocator,n->entries,&n->capacity,sizeof(TableNodeEntry)); + if ( grown == NULL ) { return 0; } + n->entries = (TableNodeEntry *)grown; + } + entry.value = visit.value; entry.type = visit.type; entry.packed_offset = 0; entry.open = 1; + visit.index = n->count; visit.close = 1; + n->entries[n->count++] = entry; + at = table_number_hash(visit.value) & (n->slot_capacity-1); + while ( n->slots[at] ) { at = (at+1) & (n->slot_capacity-1); } + n->slots[at] = n->count; + if ( n->work_count == n->work_capacity ) + { + void * grown = table_number_grow(n->allocator,n->work,&n->work_capacity,sizeof(TableNodeWork)); + if ( grown == NULL ) { return 0; } + n->work = (TableNodeWork *)grown; + } + n->work[n->work_count++] = visit; + first = n->work_count; + if ( !visit.type->edges(n,visit.value) ) { return 0; } + last = n->work_count; + // Callbacks emit edges in declaration order. Reverse only this set + // so the LIFO work stack visits its first edge first, before siblings. + while ( first < last && first < --last ) + { + TableNodeWork swap = n->work[first]; + n->work[first++] = n->work[last]; n->work[last] = swap; + } + } + table_release(n->allocator,n->work); n->work = NULL; n->work_capacity = 0; + return 1; +} + + +static SCHEMA_UNUSED const void * table_ref_at( const TableCtx * ctx, const TableRef * slot ) +{ + if ( slot->value == 0 ) { return NULL; } + if ( ctx != NULL && ctx->arena != NULL ) { return table_arena_at(ctx->arena,(uint32_t)slot->value); } + return (const uint8_t *)(const void *)slot + slot->value; +} + +static SCHEMA_UNUSED int table_number_slot( TableNumbering * n, const TableRef * slot, const TableNodeType * type ) +{ + const void * value = table_ref_at(n->ctx,slot); + if ( value == NULL ) { return 1; } + if ( !table_number_edge(n,value,type) ) { return 0; } + n->work[n->work_count-1].slot = slot; + return 1; +} + +static SCHEMA_UNUSED int table_graph_records( TableWriter * w ) +{ + uint64_t i; + const TableNumbering * n = w->nodes; + table_writer_leb(w,n->count-1); + for ( i=1; icount; i++ ) + { + const TableNodeEntry * entry=&n->entries[i]; + table_writer_id(w,entry->type->id); + if ( w->buffer == NULL ) + { + int64_t begin=w->offset; + if ( !entry->type->save(w,entry->value) ) { return 0; } + table_writer_leb(w,(uint64_t)(w->offset-begin)); + } + else + { + TableWriter probe=table_writer_probe(w); + if ( !entry->type->save(&probe,entry->value) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb(w,(uint64_t)probe.offset); + if ( !entry->type->save(w,entry->value) ) { return 0; } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int64_t table_graph_save( const TableCtx * ctx, const void * root, + const TableNodeType * type, uint8_t * buffer, int64_t capacity, TableAllocator allocator ) +{ + TableNumbering numbering; + TableWriteIds vocabulary; + TableWriter w=table_writer_make(buffer,capacity,&vocabulary); + int64_t result=-1; + if ( !table_number_build(&numbering,ctx,root,type,allocator) ) { table_number_dispose(&numbering); return -1; } + w.nodes=&numbering; + table_writer_put8(&w,1); + if ( !type->save(&w,root) ) { goto done; } + w.offset--; // the root terminator follows the numbering's final field + if ( numbering.count>1 ) + { + table_writer_id(&w,UINT64_MAX); table_writer_put8(&w,12); + if ( buffer == NULL ) + { + int64_t begin=w.offset; + if ( !table_graph_records(&w) ) { goto done; } + table_writer_leb(&w,(uint64_t)(w.offset-begin)); + } + else + { + TableWriter probe=table_writer_probe(&w); + if ( !table_graph_records(&probe) ) { goto done; } + table_writer_rewind(&probe); table_writer_leb(&w,(uint64_t)probe.offset); + if ( !table_graph_records(&w) ) { goto done; } + } + } + table_writer_put8(&w,0); table_writer_finish(&w); + if ( !w.overflow ) { result=w.offset; } +done: + table_number_dispose(&numbering); return result; +} + +static SCHEMA_UNUSED uint8_t * table_graph_pack( const TableCtx * ctx, const void * root, + const TableNodeType * type, int64_t * bytes ) +{ + TableNumbering n; + uint64_t i; + int64_t total=0; + uint8_t * packed=NULL; + *bytes=0; + if ( !table_number_build(&n,ctx,root,type,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()) ) { table_number_dispose(&n); return NULL; } + for ( i=0; iINT64_MAX-total ) { goto done; } + n.entries[i].packed_offset=(uint64_t)total; total+=size; + } + if ( (uint64_t)total>SIZE_MAX ) { goto done; } + packed=(uint8_t *)table_allocate(n.allocator,total); + if ( packed==NULL ) { goto done; } + memset(packed,0,(size_t)total); + for ( i=0; ipacked_offset; + n.pack_base=packed; + if(entry->type->blob) { + memcpy(target,entry->value,(size_t)(8+(int64_t)((const TableBlob *)entry->value)->length)); + } else { + const uint16_t one=1; + int order=*(const uint8_t *)(const void *)&one ? 1 : 2; + int64_t at=0; + if(!entry->type->cook_body(&n,target,entry->value,order) || + (entry->type->has_extent && !entry->type->cook_extent(&n,entry->value,target,target+table_align_up64(entry->type->size),&at,order))) { + table_release(n.allocator,packed); packed=NULL; goto done; + } + } + } + *bytes=total; +done: + table_number_dispose(&n); return packed; +} +typedef struct TableNodeDirEntry { uint64_t offset, type_id; } TableNodeDirEntry; +typedef struct TableNodeMap +{ + uint8_t * base; + const TableNodeDirEntry * entries; + uint64_t count; + int good, arena, refused; + TableSink * sink; +} TableNodeMap; + +static SCHEMA_UNUSED void table_node_resolve( const TableNodeMap * nodes, TableRef * slot, + uint64_t index, uint64_t target, TableReport * report ) +{ + const TableNodeDirEntry * entry; + slot->value = 0; + if ( index == 0 || nodes == NULL || !nodes->good ) { return; } + if ( index > nodes->count ) { report->malformed = 1; return; } + entry = &nodes->entries[index-1]; + if ( entry->offset == UINT64_MAX ) { return; } + if ( entry->type_id != target ) { report->kind_mismatch++; return; } + slot->value = nodes->arena ? (int64_t)entry->offset + : (int64_t)((nodes->base + entry->offset) - (uint8_t *)(void *)slot); +} + +typedef struct TableNodeScan +{ + TableReader fields, payload; + uint64_t declared, records; + int opened, present, malformed; +} TableNodeScan; + +static SCHEMA_UNUSED TableNodeScan table_node_scan_begin( const TableReader * root ) +{ + TableNodeScan scan; + memset(&scan,0,sizeof(scan)); scan.fields = *root; scan.fields.offset = 0; + return scan; +} + +static SCHEMA_UNUSED int table_node_scan_open( TableNodeScan * scan ) +{ + TableReader * fields = &scan->fields; + if ( scan->opened ) { return 0; } + scan->opened = 1; + for ( ;; ) + { + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb(fields,&ref) || ref == 0 || ref > fields->id_count ) { break; } + id = table_reader_id_at(fields,ref); + if ( !table_reader_has(fields,1) ) { break; } + kind = table_reader_get8(fields); + if ( id == UINT64_MAX ) + { + scan->present = 1; + if ( kind != 12 || !table_reader_span(fields,&scan->payload) ) + { scan->malformed = 1; return 0; } + } + else if ( !table_reader_skip(fields,kind) ) { break; } + } + if ( !scan->present ) { return 0; } + if ( !table_reader_leb(&scan->payload,&scan->declared) ) { scan->malformed = 1; return 0; } + return 1; +} + +static SCHEMA_UNUSED int table_node_scan_next( TableNodeScan * scan, uint64_t * type_id, TableReader * body ) +{ + uint64_t ref; + if ( !scan->opened && !table_node_scan_open(scan) ) { return 0; } + if ( scan->malformed || scan->payload.offset >= scan->payload.size ) { return 0; } + if ( !table_reader_leb(&scan->payload,&ref) || ref == 0 || ref > scan->payload.id_count || + !table_reader_span(&scan->payload,body) ) { scan->malformed = 1; return 0; } + *type_id = table_reader_id_at(&scan->payload,ref); scan->records++; + return 1; +} + +static SCHEMA_UNUSED int table_node_scan_whole( const TableNodeScan * scan ) +{ + return !scan->malformed && (!scan->present || scan->declared == scan->records); +} +#endif +` + +func (g *tableGen) graphType(name string) string { return g.sym(name, "node_type") } + +func (g *tableGen) emitGraphDeclarations(members []*ir.Struct) { + for _, st := range g.varMembers(members) { + g.emitStaticAssert(g.sym(st.Name, "arena_alignment"), fmt.Sprintf("SCHEMA_TABLE_ALIGNOF(%s) <= kTableAlign", st.Name), "a table node's alignment must fit the arena's") + g.pf("static const TableNodeType %s;\n", g.graphType(st.Name)) + g.pf("static SCHEMA_UNUSED int %s( TableNumbering * numbering, const void * storage );\n", g.sym(st.Name, "graph_edges")) + } +} + +func (g *tableGen) emitGraphBodies(members []*ir.Struct) { + g.emitExtentBodies(members) + for _, st := range g.varMembers(members) { + g.pf("static SCHEMA_UNUSED int %s( TableWriter * w, const void * storage )\n{ return %s(w,(const %s *)storage); }\n", g.sym(st.Name, "node_save"), g.api(st.Name, "save_body"), st.Name) + g.pf("static SCHEMA_UNUSED int %s( TableNumbering * numbering, const void * storage )\n{\n", g.sym(st.Name, "graph_edges")) + g.pf(" const %s * value=(const %s *)storage;\n (void)value; (void)numbering;\n", st.Name, st.Name) + guards := tableGuardExprs(st) + step := 0 + for _, f := range st.Fields { + if guard := guards[f.Name]; guard != "" { + g.pf(" if (%s) {\n", guard) + } + g.emitGraphEdge(f, "value->"+f.Name, " ", &step) + if guards[f.Name] != "" { + g.pf(" }\n") + } + } + g.pf(" return 1;\n}\n") + extent, hasExtent := "NULL", 0 + if g.hasCookExtent(st) { + extent = g.sym(st.Name, "cook_extent") + hasExtent = 1 + } + g.pf("static const TableNodeType %s = { UINT64_C(0x%016x), sizeof(%s), %s, %s, 0, %s, %s, %d, %d, %s, %s };\n\n", g.graphType(st.Name), ir.TableWireId(st.WireName()), st.Name, g.sym(st.Name, "node_save"), g.sym(st.Name, "graph_edges"), g.sym(st.Name, "extent"), g.sym(st.Name, "wire_storage"), ir.RecordLayout(g.unit, st).Align, hasExtent, g.sym(st.Name, "cook_body"), extent) + } +} + +func (g *tableGen) emitGraphEdge(f *ir.Field, value, ind string, step *int) { + if f.Type.Optional { + g.pf("%sif (%s_present) {\n", ind, value) + ind += " " + } + switch { + case f.IsList() || f.IsMap(): + *step++ + label := fmt.Sprintf("edges%d", *step) + typ := g.sequenceType(f) + g.pf("%s{ TableSequenceCursor %s=%s; int32_t %s_i;\n", ind, label, g.sequenceCursor(f, "numbering", value), label) + g.pf("%s if(!%s.ok) { return 0; } for(%s_i=0;%s_i<%s.count;%s_i++) {\n", ind, label, label, label, value, label) + g.pf("%s const %s * %s_value=(const %s *)table_sequence_next(&%s); if(%s_value==NULL) { return 0; }\n", ind, typ, label, typ, label, label) + g.emitGraphEdge(sequenceElement(f), "(*"+label+"_value)", ind+" ", step) + g.pf("%s }\n%s}\n", ind, ind) + case f.Array != ir.ArrayNone: + *step++ + index := fmt.Sprintf("edge_i%d", *step) + count := fmt.Sprint(f.ArrayBound) + if f.Array == ir.ArrayCounted { + count = value + "_count" + g.pf("%sif (%s<0 || %s>%d) { return 0; }\n", ind, count, count, f.ArrayBound) + } + g.pf("%s{ int32_t %s; for (%s=0; %s<%s; %s++) {\n", ind, index, index, index, count, index) + elem := *f + elem.Array = ir.ArrayNone + elem.Type.Optional = false + g.emitGraphEdge(&elem, value+"["+index+"]", ind+" ", step) + g.pf("%s} }\n", ind) + case f.Type.Pointer: + g.pf("%sif (!table_number_slot(numbering,&%s,&%s)) { return 0; }\n", ind, value, g.graphFieldType(f)) + default: + switch ref := f.Type.Ref.(type) { + case *ir.Struct: + if g.isVar(ref.Name) { + g.pf("%sif (!%s(numbering,&%s)) { return 0; }\n", ind, g.sym(ref.Name, "graph_edges"), value) + } + case *ir.Union: + g.pf("%sswitch (%s.type) {\n", ind, value) + for _, v := range ref.Variants { + g.pf("%scase %s: {\n", ind, enumConst(ref.Name+"Type", v.Name)) + if !v.Void() { + g.emitGraphEdge(v.F, armValue(value, v), ind+" ", step) + } + g.pf("%s break; }\n", ind) + } + g.pf("%scase %s: break;\n%sdefault: return 0;\n%s}\n", ind, enumNoneConst(ref.Name+"Type"), ind, ind) + } + } + if f.Type.Optional { + g.pf("%s}\n", strings.TrimSuffix(ind, " ")) + } +} + +func (g *tableGen) emitGraphLock(st *ir.Struct) { + n := st.Name + g.pf("static SCHEMA_UNUSED int %s(%sBuilder * builder)\n{\n", g.api(n, "builder_lock"), n) + g.pf(" TableCtx ctx; const %s * root; uint8_t * packed; int64_t bytes;\n", n) + g.pf(" if (builder->arena.locked) { return builder->region!=NULL; }\n if (builder->root_ref.value==0) { return 0; }\n") + g.pf(" ctx.arena=&builder->arena; root=(const %s *)table_arena_at(&builder->arena,(uint32_t)builder->root_ref.value);\n", n) + g.pf(" packed=table_graph_pack(&ctx,root,&%s,&bytes);\n", g.graphType(n)) + g.pf(" if (packed==NULL) { return 0; }\n builder->region=packed; builder->region_bytes=bytes; builder->arena.locked=1;\n table_arena_shutdown(&builder->arena); return 1;\n}\n\n") +} + +func (g *tableGen) emitGraphPublic(st *ir.Struct) { + n := st.Name + // Dispatch is generated per root: a node type outside its reachable schema + // remains unknown even if another root in the same unit can name it. + reachable := ir.PointerReachable(st) + g.pf("static SCHEMA_UNUSED const TableNodeType * %s(uint64_t id)\n{\n switch (id) {\n", g.sym(n, "node_lookup")) + for _, t := range reachable { + g.pf(" case UINT64_C(0x%016x): return &%s;\n", ir.TableWireId(t.WireName()), g.graphType(t.Name)) + } + bytesBlob, stringBlob := ir.PointerReachableBlobs(st) + if bytesBlob { + g.pf(" case kTableBytesTypeId: return &table_bytes_node_type;\n") + } + if stringBlob { + g.pf(" case kTableStringTypeId: return &table_string_node_type;\n") + } + g.pf(" default: return NULL;\n }\n}\n") + for _, measure := range []bool{true, false} { + verb, args, buffer, capacity := "save", ", uint8_t * buffer, int64_t capacity", "buffer", "capacity" + if measure { + verb, args, buffer, capacity = "measure", "", "NULL", "INT64_MAX" + } + g.pf("static SCHEMA_UNUSED int64_t %s(const TableCtx * ctx, const %s * root%s, TableAllocator allocator)\n{\n", g.api(n, verb+"_with_allocator"), n, args) + if !measure { + g.pf(" if (buffer==NULL || capacity<0) { return -1; }\n") + } + g.pf(" return table_graph_save(ctx,root,&%s,%s,%s,allocator);\n}\n", g.graphType(n), buffer, capacity) + callArgs := "" + if !measure { + callArgs = ",buffer,capacity" + } + g.pf("static SCHEMA_UNUSED int64_t %s(const TableCtx * ctx,const %s * root%s)\n{ return %s(ctx,root%s,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); }\n", g.api(n, verb), n, args, g.api(n, verb+"_with_allocator"), callArgs) + + } + g.pf("static SCHEMA_UNUSED int64_t %s(const TableReader * reader, int64_t * data_out, int64_t * records_out)\n{\n", g.sym(n, "load_layout")) + g.pf(" TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id;\n int64_t data=%s(reader), records=0;\n if(data<0) { return -1; }\n", g.sym(n, "wire_storage")) + g.pf(" while (table_node_scan_next(&scan,&id,&body)) {\n const TableNodeType * type=%s(id);\n", g.sym(n, "node_lookup")) + g.pf(" if (type!=NULL) { int64_t size=table_node_wire_storage(type,&body); if (size<0 || size>INT64_MAX-data) { return -1; } data+=size; }\n records++;\n }\n") + g.pf(" if (records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1 || (records+1)*(int64_t)sizeof(TableNodeDirEntry)>INT64_MAX-data) { return -1; }\n") + g.pf(" *data_out=data; *records_out=records; return data+(records+1)*(int64_t)sizeof(TableNodeDirEntry);\n}\n") + g.pf("static SCHEMA_UNUSED int64_t %s(const uint8_t * wire, int64_t bytes, int64_t * attribution, TableRefuseReason * reason)\n{\n", g.api(n, "load_measure_ex")) + g.pf(" TableReport report; TableReader reader; int64_t data,records,total; memset(&report,0,sizeof(report));\n if (attribution!=NULL) { *attribution=0; }\n") + g.pf(" if (!table_wire_open(&reader,wire,bytes,&report)) { if(report.refused && reason!=NULL) { *reason=SCHEMA_TABLE_REFUSE_UNKNOWN_FORM; } return -1; }\n") + g.pf(" report.reason=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH; total=%s(&reader,&data,&records);\n if(total<0 && reason!=NULL) { *reason=report.reason; }\n if(total>=0 && attribution!=NULL) { *attribution=(records+1)*(int64_t)sizeof(TableNodeDirEntry); } return total;\n}\n", g.sym(n, "load_layout")) + g.pf("static SCHEMA_UNUSED int64_t %s(const uint8_t * wire, int64_t bytes)\n{ return %s(wire,bytes,NULL,NULL); }\n", g.api(n, "load_measure"), g.api(n, "load_measure_ex")) + g.emitGraphLoad(st) +} + +func (g *tableGen) emitGraphLoad(st *ir.Struct) { + n := st.Name + // One fill procedure serves caller-owned regions and editable arenas. Both + // passes use the resident directory; following a reference never decodes it. + g.pf("static SCHEMA_UNUSED int %s(TableReader * reader, %s * root, TableNodeMap * nodes, TableNodeDirEntry * directory, TableSink * sink)\n{\n", g.sym(n, "load_graph"), n) + g.pf(" TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id,k=0; int32_t unknown=0;\n %s(root);\n", g.api(n, "reset")) + g.pf(" while (table_node_scan_next(&scan,&id,&body)) {\n const TableNodeType * type=%s(id);\n directory[k+1].type_id=id; directory[k+1].offset=UINT64_MAX;\n", g.sym(n, "node_lookup")) + g.pf(" if (type!=NULL && type->blob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; }\n if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; k++; continue; }\n if (type==NULL) { unknown++; }\n else if (sink->region!=NULL) {\n int64_t at=sink->region->used, size=table_node_wire_storage(type,&body);\n if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; return 0; }\n directory[k+1].offset=(uint64_t)at; sink->region->used+=size;\n } else {\n uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size);\n if(at==kTableAllocFailed) { reader->report->malformed=1; return 0; }\n directory[k+1].offset=at;\n }\n k++;\n }\n") + retainedUnknown := "" + if g.retain { + retainedUnknown = "if(retention.retain)reader->report->retain_lost+=unknown;" + } + g.pf(" nodes->good=table_node_scan_whole(&scan);\n if(nodes->good) { reader->report->unknown+=unknown; %s } else { reader->report->malformed=1; }\n if(nodes->good) {\n scan=table_node_scan_begin(reader); k=0;\n while(table_node_scan_next(&scan,&id,&body)) {\n if(directory[k+1].offset!=UINT64_MAX) {\n void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset;\n TableRegionSink extent; TableSink carve; const TableNodeType * type=%s(id);\n carve.worker=sink->worker; carve.region=NULL;\n if(!nodes->arena) { extent.base=(uint8_t *)value; extent.used=type->size; extent.capacity=table_node_wire_storage(type,&body); carve.region=&extent; }\n nodes->sink=&carve; body.nodes=nodes;\n switch(id) {\n", retainedUnknown, g.sym(n, "node_lookup")) + bytesBlob, stringBlob := ir.PointerReachableBlobs(st) + for _, blob := range []struct { + enabled bool + name string + }{{bytesBlob, "kTableBytesTypeId"}, {stringBlob, "kTableStringTypeId"}} { + if blob.enabled { + g.pf(" case %s: { TableBlob * blob=(TableBlob *)value; blob->length=(uint32_t)body.size; blob->zero=0; memcpy(blob+1,body.buffer,(size_t)body.size); break; }\n", blob.name) + } + } + for _, t := range ir.PointerReachable(st) { + if g.retain { + g.pf(" case UINT64_C(0x%016x):retention.path=table_retain_root(value,(uint32_t)k+2);%s(&body,(%s *)value);break;\n", ir.TableWireId(t.WireName()), g.api(t.Name, "load_body"), t.Name) + } else { + g.pf(" case UINT64_C(0x%016x): %s(&body,(%s *)value); break;\n", ir.TableWireId(t.WireName()), g.api(t.Name, "load_body"), t.Name) + } + } + retainedRoot := "" + if g.retain { + retainedRoot = "retention.path=table_retain_root(root,1);" + } + g.pf(" default: break;\n }\n nodes->sink=NULL; if(nodes->refused) { return 0; }\n }\n k++;\n }\n }\n reader->nodes=nodes; reader->nested=0;\n { int read_ok; TableRegionSink extent; TableSink carve; carve.worker=sink->worker; carve.region=NULL;\n if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; extent.used=sizeof(*root); extent.capacity=%s(reader); carve.region=&extent; }\n nodes->sink=&carve; %s read_ok=%s(reader,root); nodes->sink=NULL; return !nodes->refused && (!nodes->arena || read_ok); }\n}\n", g.sym(n, "wire_storage"), retainedRoot, g.api(n, "load_body")) + if g.retain { + return + } + g.pf("static SCHEMA_UNUSED const %s * %s(uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report)\n{\n", n, g.api(n, "load")) + g.pf(" TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableRegionSink place; TableSink sink; int64_t data,records,total;\n memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; }\n if(!table_wire_open(&reader,wire,wire_bytes,report)) { return NULL; }\n") + g.pf(" total=%s(&reader,&data,&records);\n", g.sym(n, "load_layout")) + g.pf(" if(total<0 || region==NULL || region_bytesmalformed=1; return NULL; }\n memset(region,0,(size_t)total); directory=(TableNodeDirEntry *)(void *)(region+data);\n") + g.pf(" directory[0].offset=0; directory[0].type_id=UINT64_C(0x%016x);\n", ir.TableWireId(st.WireName())) + g.pf(" nodes.base=region; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=0; nodes.refused=0; nodes.sink=NULL;\n place.base=region; place.capacity=data; place.used=%s(&reader); sink.region=&place; sink.worker=NULL;\n", g.sym(n, "wire_storage")) + g.pf(" if(!%s(&reader,(%s *)(void *)region,&nodes,directory,&sink)) { return NULL; }\n return (const %s *)(const void *)region;\n}\n", g.sym(n, "load_graph"), n, n) + g.pf("static SCHEMA_UNUSED int %s(%sBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report)\n{\n", g.api(n, "load_builder"), n) + g.pf(" TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableSink sink; TableNodeScan scan; TableReader body; uint64_t id; int64_t records=0; %s * root; int ok;\n memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; }\n if(!table_wire_open(&reader,wire,wire_bytes,report)) { return 0; }\n", n) + g.pf(" scan=table_node_scan_begin(&reader); while(table_node_scan_next(&scan,&id,&body)) { records++; }\n if(records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1) { report->malformed=1; return 0; }\n") + g.pf(" root=%s(builder); if(root==NULL) { report->malformed=1; return 0; }\n", g.api(n, "builder_root")) + g.pf(" directory=(TableNodeDirEntry *)table_allocate(builder->arena.allocator,(records+1)*(int64_t)sizeof(TableNodeDirEntry));\n if(directory==NULL) { report->malformed=1; return 0; }\n directory[0].offset=(uint64_t)builder->root_ref.value; directory[0].type_id=UINT64_C(0x%016x);\n", ir.TableWireId(st.WireName())) + g.pf(" nodes.base=NULL; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=1; nodes.refused=0; nodes.sink=NULL; sink.region=NULL; sink.worker=&builder->main;\n ok=%s(&reader,root,&nodes,directory,&sink); table_release(builder->arena.allocator,directory); return ok;\n}\n", g.sym(n, "load_graph")) +} diff --git a/internal/codegen/ctable/graph_json.go b/internal/codegen/ctable/graph_json.go new file mode 100644 index 000000000..38d9b3c26 --- /dev/null +++ b/internal/codegen/ctable/graph_json.go @@ -0,0 +1,451 @@ +package ctable + +// Pointer JSON shares the descriptor walk with the fixed class. The map is +// per operation: text labels on read, reference counts and labels on write. +const tableJsonGraphSource = ` +/* ---- json graph walk: begin ---- */ +typedef struct TableJsonGraphEntry +{ + uint64_t key; + int64_t count, label; + int open; + uint32_t node; + const TableTypeInfo * type; +} TableJsonGraphEntry; + +typedef struct TableJsonGraphMap +{ + TableJsonGraphEntry * entries; + uint64_t capacity, count; + TableAllocator allocator; +} TableJsonGraphMap; + +static SCHEMA_UNUSED uint64_t table_json_graph_slot( const TableJsonGraphMap * map, uint64_t key ) +{ + uint64_t hash = key * UINT64_C(0x9E3779B97F4A7C15); + uint64_t at; + hash ^= hash >> 29; at = hash & (map->capacity-1); + while ( map->entries[at].key != 0 && map->entries[at].key != key ) { at = (at+1) & (map->capacity-1); } + return at; +} + +static SCHEMA_UNUSED TableJsonGraphEntry * table_json_graph_find( TableJsonGraphMap * map, uint64_t key ) +{ + TableJsonGraphEntry * entry; + if ( map->capacity == 0 ) { return NULL; } + entry = &map->entries[table_json_graph_slot(map,key)]; + return entry->key == key ? entry : NULL; +} + +static SCHEMA_UNUSED TableJsonGraphEntry * table_json_graph_reach( TableJsonGraphMap * map, uint64_t key, int * taken ) +{ + TableJsonGraphEntry * entry; + if ( map->capacity == 0 || map->count >= map->capacity - map->capacity/4 - 1 ) + { + TableJsonGraphMap grown; + uint64_t i; + grown.capacity = map->capacity ? map->capacity*4 : 64; + if ( grown.capacity < map->capacity || grown.capacity > INT64_MAX / sizeof(TableJsonGraphEntry) ) { return NULL; } + grown.count = map->count; grown.allocator=map->allocator; + grown.entries = (TableJsonGraphEntry *)table_allocate(map->allocator,(int64_t)grown.capacity*sizeof(TableJsonGraphEntry)); + if ( grown.entries == NULL ) { return NULL; } + for ( i = 0; i < map->capacity; i++ ) + { + if ( map->entries[i].key ) { grown.entries[table_json_graph_slot(&grown,map->entries[i].key)] = map->entries[i]; } + } + table_release(map->allocator,map->entries); *map = grown; + } + entry = &map->entries[table_json_graph_slot(map,key)]; + *taken = entry->key != key; + if ( *taken ) { entry->key = key; map->count++; } + return entry; +} + +typedef struct TableJsonGraphIn +{ + TableWorker * worker; + TableJsonGraphMap labels; +} TableJsonGraphIn; + +static SCHEMA_UNUSED int table_json_scan_label( TableJsonIn * in, uint64_t * label ) +{ + uint64_t value = 0; + table_json_space(in); + if ( in->pos >= in->size || in->text[in->pos] < '1' || in->text[in->pos] > '9' ) { in->bad = 1; return 0; } + while ( in->pos < in->size && in->text[in->pos] >= '0' && in->text[in->pos] <= '9' ) + { + uint64_t digit = (uint64_t)(in->text[in->pos]-'0'); + if ( value > (UINT64_MAX-digit)/10 ) { in->bad = 1; return 0; } + value = value*10+digit; in->pos++; + } + *label = value; return 1; +} + +static SCHEMA_UNUSED void * table_json_emplace( TableJsonGraphIn * graph, void * slot, const TableTypeInfo * type ) +{ + uint32_t at = table_worker_bump(graph->worker,type->size); + void * node; + if ( at == kTableAllocFailed ) { return NULL; } + node = table_arena_at(graph->worker->arena,at); + type->reset(node); ((TableRef *)slot)->value = at; return node; +} + +static SCHEMA_UNUSED int table_json_read_blob( TableJsonIn * in, void * slot, const TableFieldInfo * f ) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; + TableRef * ref=(TableRef *)slot; + int64_t mark, symbols=0, length, placed=0, at; + int malformed=0, held=0; + uint32_t accumulator=0; + uint8_t * data; + if(graph==NULL || table_json_peek(in)!='"') { in->bad=1; return 0; } + ref->value=0; + if(strcmp(f->type_name,"string")==0) + { + int32_t count=0, written=0; + char * text; + mark=in->pos; + if(!table_json_scan_string(in,NULL,0,&count)) { return 0; } + in->pos=mark; text=table_string_emplace(graph->worker,ref,NULL,count); + if(text==NULL) { in->bad=1; return 0; } + return table_json_scan_string(in,text,count,&written); + } + mark=++in->pos; + for(;;) + { + char c; + if(in->pos>=in->size) { in->bad=1; return 0; } + c=in->text[in->pos++]; + if(c=='"') { break; } + if(c=='=' || malformed) { continue; } + if(table_json_base64_value((uint8_t)c)<0) { malformed=1; continue; } + symbols++; + } + if(malformed) { in->report->kind_mismatch++; return 1; } + length=symbols*6/8; + data=table_bytes_emplace(graph->worker,ref,length); + if(data==NULL) { in->bad=1; return 0; } + for(at=mark; in->text[at]!='"'; at++) + { + int32_t symbol=table_json_base64_value((uint8_t)in->text[at]); + if(symbol<0) { continue; } + accumulator=(accumulator<<6)|(uint32_t)symbol; held+=6; + if(held>=8) { held-=8; if(placed>held)&0xff); } } + } + return 1; +} + +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ + TableJsonGraphIn * graph = (TableJsonGraphIn *)in->graph; + TableJsonGraphEntry * entry; + char key[kTableJsonMaxKey], c; + int32_t length = 0; + uint64_t label = 0; + int taken = 0, bare; + void * node; + if(graph!=NULL && f->table==NULL) { return table_json_read_blob(in,slot,f); } + if ( graph == NULL || depth+1 > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; c = table_json_peek(in); + if ( c == '}' ) + { + in->pos++; + if ( table_json_emplace(graph,slot,f->table) == NULL ) { in->bad = 1; return 0; } + return 1; + } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + if ( strcmp(key,"&node") != 0 ) + { + node = table_json_emplace(graph,slot,f->table); + if ( node == NULL ) { in->bad = 1; return 0; } + return table_json_read_table_keys(in,node,f->table,depth+1,key); + } + if ( !table_json_scan_label(in,&label) ) { return 0; } + entry = table_json_graph_reach(&graph->labels,label,&taken); + if ( entry == NULL ) { in->bad = 1; return 0; } + c = table_json_peek(in); + if ( c == ',' ) { in->pos++; c = table_json_peek(in); } + bare = c == '}'; + if ( bare == taken ) { in->bad = 1; return 0; } + if ( bare ) + { + in->pos++; + if ( entry->open ) { in->bad = 1; return 0; } + ((TableRef *)slot)->value = 0; + if ( entry->type == NULL ) { return 1; } + if ( entry->type != f->table ) { in->report->kind_mismatch++; return 1; } + ((TableRef *)slot)->value = entry->node; return 1; + } + node = table_json_emplace(graph,slot,f->table); + if ( node == NULL ) { in->bad = 1; return 0; } + entry->open = 1; + if ( !table_json_read_table_keys(in,node,f->table,depth+1,NULL) ) { return 0; } + entry = table_json_graph_find(&graph->labels,label); + if ( entry == NULL ) { in->bad = 1; return 0; } + entry->node = (uint32_t)((TableRef *)slot)->value; entry->type = f->table; entry->open = 0; return 1; +} + +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ + TableJsonGraphIn * graph = (TableJsonGraphIn *)in->graph; + uint64_t label = 0; + int taken = 0; + if ( graph == NULL || strcmp(key,"&node") != 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_label(in,&label) ) { return 0; } + if ( table_json_graph_reach(&graph->labels,label,&taken) == NULL ) { in->bad = 1; return 0; } + return 1; +} + +typedef struct TableJsonGraphOut +{ + TableJsonGraphMap nodes; + int counting; + int64_t next_label; +} TableJsonGraphOut; + +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ + TableJsonGraphOut * graph = (TableJsonGraphOut *)out->graph; + const void * node = table_ref_at(NULL,(const TableRef *)slot); + TableJsonGraphEntry * entry; + int taken = 0, any = 1; + int64_t before; + if ( graph == NULL ) { return 0; } + if ( node == NULL ) { table_json_raw(out,"null",4); return 1; } + entry = table_json_graph_reach(&graph->nodes,(uint64_t)(uintptr_t)node,&taken); + if ( entry == NULL ) { return 0; } + if(f->table==NULL) + { + const TableBlob * blob=(const TableBlob *)node; + if(graph->counting) { entry->count++; return 1; } + if(entry->count>1 || blob->length>INT32_MAX) { return 0; } + if(strcmp(f->type_name,"string")==0) { table_json_write_string(out,(const char *)(blob+1),(int32_t)blob->length); } + else { table_json_write_base64(out,(const uint8_t *)(blob+1),(int32_t)blob->length); } + return 1; + } + if ( graph->counting ) + { + entry->count++; + if ( !taken ) { return entry->open == 0; } + entry->open = 1; + if ( !table_json_write_value(out,node,f->table,depth) ) { return 0; } + entry = table_json_graph_find(&graph->nodes,(uint64_t)(uintptr_t)node); + if ( entry == NULL ) { return 0; } + entry->open = 0; return 1; + } + if ( entry->count <= 1 ) { return table_json_write_value(out,node,f->table,depth); } + if ( depth > kTableJsonMaxDepth ) { return 0; } + table_json_put(out,'{'); table_json_line(out,depth+1); table_json_raw(out,"\"&node\": ",9); + if ( entry->label ) + { + table_json_write_unsigned(out,(uint64_t)entry->label); + table_json_line(out,depth); table_json_put(out,'}'); return 1; + } + entry->label = ++graph->next_label; + table_json_write_unsigned(out,(uint64_t)entry->label); + before = out->offset; + if ( !table_json_write_fields(out,node,f->table,depth,&any) || out->offset == before ) { return 0; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + +static SCHEMA_UNUSED int table_json_read_graph( TableWorker * worker, void * root, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) +{ + TableJsonGraphIn graph; + TableJsonIn in; + TableReport ignored = {0}; + int ok; + memset(&graph,0,sizeof(graph)); graph.worker = worker; graph.labels.allocator=worker->arena!=NULL ? worker->arena->allocator : table_default_allocator(); + memset(&in,0,sizeof(in)); in.text = text; in.size = bytes; in.report = report ? report : &ignored; in.graph = &graph; + if ( root == NULL || worker->arena == NULL || text == NULL || bytes < 0 ) { in.report->malformed = 1; return 0; } + info->reset(root); + ok = table_json_read_table(&in,root,info,0); + if ( ok ) { table_json_space(&in); if ( in.pos != in.size ) { in.bad = 1; } } + table_release(graph.labels.allocator,graph.labels.entries); + if ( in.bad || !ok ) { in.report->malformed = 1; return 0; } + return 1; +} + +static SCHEMA_UNUSED int64_t table_json_write_graph( const void * root, const TableTypeInfo * info, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + TableJsonGraphOut graph; + TableJsonOut count, out; + TableJsonGraphEntry * entry; + int taken = 0, ok; + if ( root == NULL ) { return -1; } + memset(&graph,0,sizeof(graph)); graph.counting = 1; graph.nodes.allocator=allocator; + entry = table_json_graph_reach(&graph.nodes,(uint64_t)(uintptr_t)root,&taken); + if ( entry == NULL ) { return -1; } + entry->open = 1; + memset(&count,0,sizeof(count)); count.graph = &graph; + ok = table_json_write_value(&count,root,info,0); + graph.counting = 0; + memset(&out,0,sizeof(out)); out.buffer = buffer; out.capacity = capacity; out.graph = &graph; + if ( ok ) { ok = table_json_write_value(&out,root,info,0); } + table_release(graph.nodes.allocator,graph.nodes.entries); + if ( !ok ) { return -1; } + table_json_put(&out,'\n'); return out.overflow ? -1 : out.offset; +} +/* ---- json graph walk: end ---- */ + +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth) +{ + const TableSequence * list=(const TableSequence *)storage; + TableSequenceCursor cursor=table_sequence_cursor(NULL,list,f->elem_size); int32_t i; + if(!cursor.ok) { return 0; } + table_json_put(out,'['); + for(i=0;icount;i++) { + const void * element=table_sequence_next(&cursor); if(element==NULL) { return 0; } + if(i) { table_json_put(out,','); } table_json_line(out,depth+1); + if(!table_json_write_scalar(out,element,f,depth+1)) { return 0; } + } + if(list->count) { table_json_line(out,depth); } table_json_put(out,']'); return 1; +} +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; + TableSequence * list=(TableSequence *)storage; char shape=table_json_element_shape(f); + if(graph==NULL || table_json_peek(in)!='[') { in->bad=1; return 0; } + memset(list,0,sizeof(*list)); in->pos++; + for(;;) { + char c=table_json_peek(in); void * element; + if(c==']') { in->pos++; return 1; } + if(c==0) { in->bad=1; return 0; } + element=table_sequence_add(graph->worker,list,f->elem_size); + if(element==NULL) { in->bad=1; return 0; } + if(f->kind==13) { f->table->reset(element); } + if(table_json_value_shape(in)!=shape && !(f->kind==17 && table_json_value_shape(in)=='z')) { + in->report->kind_mismatch++; if(!table_json_skip_value(in,depth+1)) { return 0; } + } else if(!table_json_read_scalar(in,element,f,depth+1)) { return 0; } + c=table_json_peek(in); + if(c==',') { in->pos++; continue; } + if(c==']') { in->pos++; return 1; } + in->bad=1; return 0; + } +} +#endif + +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED TableJsonInteger table_json_interpret_exact( const char * token, int32_t length ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + int32_t i = 0; + if ( i < length && token[i] == '-' ) { out.negative = 1; i++; } // WalkNumber refuses a leading plus + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // leading and trailing zeros stripped, so the last digit kept is significant + int32_t start = 0, end = int_len + frac_len; + int64_t point = (int64_t) int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + const int64_t digits = end - start; + if ( digits == 0 ) { out.negative = 0; return out; } // the value is zero, and -0 IS zero + if ( point < digits ) { out.fractional = 1; return out; } // a significant digit below the point + if ( point > 20 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + for ( int32_t k = start; k < end; k++ ) + { + const uint64_t digit = (uint64_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + out.magnitude = out.magnitude * 10 + digit; + } + for ( int64_t k = digits; k < point; k++ ) // the point's own zeros, which no digit spells + { + if ( out.magnitude > UINT64_MAX / 10 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + out.magnitude *= 10; + } + return out; +} + +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth) +{ + const TableSequence * map=(const TableSequence *)storage; + TableSequenceCursor cursor=table_sequence_cursor(NULL,map,f->elem_size); int32_t i; + const TableFieldInfo * key=&f->table->fields[0]; const TableFieldInfo * value=&f->table->fields[1]; + if(!cursor.ok) { return 0; } table_json_put(out,'{'); + for(i=0;icount;i++) { + const uint8_t * entry=(const uint8_t *)table_sequence_next(&cursor); if(entry==NULL) { return 0; } + if(i) { table_json_put(out,','); } table_json_line(out,depth+1); + if(key->kind==12) { table_json_write_string(out,(const char *)entry+key->offset,table_json_count(entry,key)); } + else { + table_json_put(out,'"'); + if(key->kind>=2 && key->kind<=5) { table_json_write_signed(out,table_json_get_signed(entry+key->offset,key->elem_size)); } + else { table_json_write_unsigned(out,table_json_get_raw(entry+key->offset,key->elem_size)); } + table_json_put(out,'"'); + } + table_json_raw(out,": ",2); if(!table_json_write_field(out,entry,value,depth+1)) { return 0; } + } + if(map->count) { table_json_line(out,depth); } table_json_put(out,'}'); return 1; +} +static SCHEMA_UNUSED int table_json_map_key_value(const char * token,int32_t length,const TableFieldInfo * key,int64_t * value,int * fits) +{ + TableReport scratch={0}; TableJsonIn probe; TableJsonInteger number; int integral=0,moved=0; + memset(&probe,0,sizeof(probe)); probe.text=token; probe.size=length; probe.report=&scratch; *fits=0; + table_json_space(&probe); if(probe.pos!=0 || !table_json_walk_number(&probe,&integral) || probe.pos!=length) { return 0; } + number=table_json_interpret_exact(token,length); if(number.fractional || number.saturated) { return 1; } + *value=table_json_integer_in_domain(number,key->kind>=2 && key->kind<=5,(int32_t)key->elem_size,&moved); *fits=!moved; return 1; +} +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; TableSequence * map=(TableSequence *)storage; + const TableFieldInfo * key=&f->table->fields[0]; const TableFieldInfo * value=&f->table->fields[1]; char shape=table_json_shape(value); + if(graph==NULL || table_json_peek(in)!='{' || depth+1>kTableJsonMaxDepth) { in->bad=1; return 0; } + memset(map,0,sizeof(*map)); in->pos++; + for(;;) { + char c=table_json_peek(in),token[kTableJsonMaxKey]; int32_t length=0,before; int64_t number=0; int place=1; void * entry; + TableReport scratch={0}; TableJsonIn scan=*in; scan.report=&scratch; + if(c=='}') { in->pos++; return 1; } if(c==0) { in->bad=1; return 0; } + if(!table_json_scan_string(&scan,token,kTableJsonMaxKey-1,&length)) { in->pos=scan.pos; in->bad=1; return 0; } + in->pos=scan.pos; token[length]=0; + if(table_json_peek(in)!=':') { in->bad=1; return 0; } in->pos++; + if(key->kind==12) { + if(scratch.clamped || length>key->array_bound) { in->report->clamped++; place=0; } + } else { + int fits=0; + if(scratch.clamped) { in->report->kind_mismatch++; place=0; } + else if(!table_json_map_key_value(token,length,key,&number,&fits)) { in->bad=1; return 0; } + else if(!fits) { in->report->kind_mismatch++; place=0; } + } + before=map->count; entry=place ? f->place(graph->worker,map,token,length,number) : NULL; + if(place && entry==NULL) { in->bad=1; return 0; } + if(entry!=NULL && map->count==before) { in->report->duplicate++; } + c=table_json_value_shape(in); + if(entry==NULL) { if(!table_json_skip_value(in,depth+1)) { return 0; } } + else if(c!=shape && !(value->kind==17 && !value->is_array && c=='z')) { + in->report->kind_mismatch++; if(!table_json_skip_value(in,depth+1)) { return 0; } + } else if(!table_json_read_field(in,entry,value,depth+1)) { return 0; } + c=table_json_peek(in); if(c==',') { in->pos++; continue; } if(c=='}') { in->pos++; return 1; } + in->bad=1; return 0; + } +} +#endif +` diff --git a/internal/codegen/ctable/json.go b/internal/codegen/ctable/json.go index 29a6b2c78..f2773aa18 100644 --- a/internal/codegen/ctable/json.go +++ b/internal/codegen/ctable/json.go @@ -31,15 +31,17 @@ func tableJsonWalk(pkg string) string { // includes the header to use the wire codecs or the descriptors pays nothing // for a form it never calls (docs/SPEC-TABLES.md §16.1, owner's ruling). func (g *tableGen) emitJsonDeclarations(st *ir.Struct) { + if st.IsMapEntry() { + return + } if g.isVar(st.Name) { - // docs/SPEC-TABLES.md §16.1 states the builder surface for this class — - // FromJson( builder, ... ) — and this backend does not carry it - // yet, exactly as the reference does not. The refusal is by ABSENCE - // and it is loud: there is no function to call, so nothing silently - // reads a pointered table as an empty one. - g.pf("/* %s is VARIABLE-LENGTH. Its text form reads through the builder\n", st.Name) - g.pf(" (docs/SPEC-TABLES.md §16.1), which this backend does not emit yet:\n") - g.pf(" no %s and no %s exist to call. */\n\n", g.api(st.Name, "from_json"), g.api(st.Name, "to_json")) + g.pf("int %s( %sBuilder * builder, const char * text, int64_t bytes, TableReport * report );\n", g.sym(st.Name, "from_json"), st.Name) + g.pf("int64_t %s( const %s * value, char * buffer, int64_t capacity, TableAllocator allocator );\n", g.sym(st.Name, "to_json"), st.Name) + g.pf("static SCHEMA_UNUSED int %s( %sBuilder * builder, const char * text, int64_t bytes, TableReport * report ) { return %s(builder,text,bytes,report); }\n", g.api(st.Name, "from_json"), st.Name, g.sym(st.Name, "from_json")) + g.pf("static SCHEMA_UNUSED int64_t %s( const %s * value ) { return %s(value,NULL,0,table_default_allocator()); }\n", g.api(st.Name, "to_json_measure"), st.Name, g.sym(st.Name, "to_json")) + g.pf("static SCHEMA_UNUSED int64_t %s( const %s * value, char * buffer, int64_t capacity ) { return %s(value,buffer,capacity,table_default_allocator()); }\n\n", g.api(st.Name, "to_json"), st.Name, g.sym(st.Name, "to_json")) + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * value,char * buffer,int64_t capacity,TableAllocator allocator) { return %s(value,buffer,capacity,allocator); }\n", g.api(st.Name, "to_json_with_allocator"), st.Name, g.sym(st.Name, "to_json")) + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * value,TableAllocator allocator) { return %s(value,NULL,0,allocator); }\n\n", g.api(st.Name, "to_json_measure_with_allocator"), st.Name, g.sym(st.Name, "to_json")) return } g.pf("/* %s in and out of a JSON text — one instance, one text, the generic\n", st.Name) @@ -57,7 +59,14 @@ func (g *tableGen) emitJsonDeclarations(st *ir.Struct) { // emitJsonDefinitions puts the same member's three definitions in the .c, each // a thin wrapper naming a descriptor and nothing else. func (g *tableGen) emitJsonDefinitions(st *ir.Struct) { + if st.IsMapEntry() { + return + } if g.isVar(st.Name) { + g.pf("int %s( %sBuilder * builder, const char * text, int64_t bytes, TableReport * report )\n{\n", g.sym(st.Name, "from_json"), st.Name) + g.pf(" return table_json_read_graph(&builder->main,%s(builder),&%s,text,bytes,report);\n}\n\n", g.api(st.Name, "builder_root"), g.sym(st.Name, "info")) + g.pf("int64_t %s( const %s * value, char * buffer, int64_t capacity, TableAllocator allocator )\n{\n", g.sym(st.Name, "to_json"), st.Name) + g.pf(" return table_json_write_graph(value,&%s,buffer,capacity,allocator);\n}\n\n", g.sym(st.Name, "info")) return } g.pf("int %s( %s * value, const char * text, int64_t bytes, TableReport * report )\n{\n", g.sym(st.Name, "from_json"), st.Name) @@ -134,7 +143,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -146,7 +155,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -191,6 +200,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -252,11 +376,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -271,11 +395,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -286,6 +412,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -340,6 +468,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -452,15 +581,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -509,6 +632,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -540,6 +707,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -582,8 +795,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -607,7 +834,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -627,7 +857,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -663,6 +893,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -692,6 +923,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -759,9 +999,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -775,24 +1015,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -802,6 +1043,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -810,8 +1052,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -900,26 +1155,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -933,29 +1181,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -965,16 +1211,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -988,10 +1234,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -1007,6 +1286,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1140,24 +1491,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1192,16 +1560,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1233,9 +1848,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1327,6 +1954,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1364,74 +1992,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) - { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1439,6 +2026,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1554,7 +2153,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1609,7 +2208,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1638,28 +2237,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1704,7 +2305,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1730,6 +2331,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1741,6 +2349,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1768,6 +2377,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and diff --git a/internal/codegen/ctable/maps.go b/internal/codegen/ctable/maps.go new file mode 100644 index 000000000..18e72e793 --- /dev/null +++ b/internal/codegen/ctable/maps.go @@ -0,0 +1,218 @@ +package ctable + +import ( + "fmt" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +func (g *tableGen) sequenceCursor(f *ir.Field, n, value string) string { + if !f.IsMap() { + return fmt.Sprintf("table_sequence_cursor(%s ? %s->ctx : NULL,&%s,sizeof(%s))", n, n, value, g.sequenceType(f)) + } + return fmt.Sprintf("table_sequence_order(%s ? %s->ctx : NULL,&%s,sizeof(%s),%s,%s ? %s->allocator : table_default_allocator(),%s ? &%s->orders : NULL)", n, n, value, g.sequenceType(f), g.sym(f.MapEntry.Name, "compare"), n, n, n, n) +} + +func (g *tableGen) mapKeyType(f *ir.Field) string { + if ir.MapKeyField(f).Type.Kind == ir.TString { + return "TableMapKey" + } + return g.cFieldType(ir.MapKeyField(f).Type) +} + +func mapPair(f *ir.Field) bool { + v := ir.MapValueField(f) + return v.Array == ir.ArrayCounted || !v.Type.Pointer && (v.Type.Kind == ir.TString || v.Type.Kind == ir.TWString || v.Type.Kind == ir.TBytes) +} + +func (g *tableGen) mapValueType(f *ir.Field) string { + v := ir.MapValueField(f) + switch { + case v.IsMap(): + return "TableMap" + case v.IsList(): + return "TableList" + case mapPair(f): + return f.MapEntry.Name + case v.Array == ir.ArrayFixed: + return f.MapEntry.Name + "Value" + case v.Type.Pointer: + return "TableRef" + default: + return g.cFieldType(v.Type) + } +} + +const tableMapRuntime = ` +#ifndef SCHEMA_TABLE_MAP_RUNTIME +#define SCHEMA_TABLE_MAP_RUNTIME +typedef TableSequence TableMap; +typedef struct TableMapKey { const char * data; int32_t length; } TableMapKey; +typedef struct TableMapIndex { int32_t * slots; int32_t capacity; int good; } TableMapIndex; +static SCHEMA_UNUSED int table_map_key_compare(TableMapKey a,TableMapKey b) +{ + int32_t common; + if(a.length<0 || b.length<0) { return a.length<0 ? -1 : 1; } + common=a.lengthb.length ? 1 : 0)); +} +static SCHEMA_UNUSED TableMapKey table_map_key(const char * data,int32_t bound) +{ + TableMapKey key; key.data=data; key.length=0; + if(data==NULL) { key.length=-1; return key; } + while(key.length<=bound && data[key.length]) { key.length++; } return key; +} +static SCHEMA_UNUSED uint64_t table_map_hash(const void * data,int64_t bytes) +{ + const uint8_t * p=(const uint8_t *)data; uint64_t hash=UINT64_C(14695981039346656037); int64_t i; + for(i=0;iINT32_MAX/2) { return 0; } + while(slots<(int64_t)count*2) { slots*=2; } return slots<=INT32_MAX ? (int32_t)slots : 0; +} +#endif +` + +func (g *tableGen) emitMapHelpers(owner *ir.Struct, f *ir.Field) { + n := f.MapEntry.Name + key := ir.MapKeyField(f) + ktype := g.mapKeyType(f) + stringKey := key.Type.Kind == ir.TString + value := ir.MapValueField(f) + vtype := g.mapValueType(f) + surface := owner.Name + ir.GoExportName(f.Name) + if value.Array == ir.ArrayFixed { + g.pf("typedef %s %s[%d];\n", g.sequenceType(value), vtype, value.ArrayBound) + } + g.pf("static SCHEMA_UNUSED %s %s(const %s * entry)\n{\n", ktype, g.sym(n, "key"), n) + if stringKey { + g.pf(" TableMapKey key={entry->key,entry->key_length}; return key;\n") + } else { + g.pf(" return entry->key;\n") + } + g.pf("}\n") + g.pf("static SCHEMA_UNUSED int %s(%s a,%s b)\n{ return ", g.sym(n, "key_compare"), ktype, ktype) + if stringKey { + g.pf("table_map_key_compare(a,b)") + } else { + g.pf("ab ? 1 : 0)") + } + g.pf("; }\n") + g.pf("static SCHEMA_UNUSED int %s(const void * a,const void * b)\n{ return %s(%s((const %s *)a),%s((const %s *)b)); }\n", g.sym(n, "compare"), g.sym(n, "key_compare"), g.sym(n, "key"), n, g.sym(n, "key"), n) + g.pf("static SCHEMA_UNUSED %s * %s(const TableCtx * ctx,const TableMap * map,%s key)\n{\n", n, g.sym(n, "find"), ktype) + g.pf(" if(ctx!=NULL && ctx->arena!=NULL) { TableSequenceCursor cursor=table_sequence_cursor(ctx,map,sizeof(%s)); const %s * entry;\n while((entry=(const %s *)table_sequence_next(&cursor))!=NULL) { if(%s(%s(entry),key)==0) { return (%s *)(uintptr_t)entry; } }\n } else {\n", n, n, n, g.sym(n, "key_compare"), g.sym(n, "key"), n) + g.pf(" int32_t lo=0,hi=map->count; const %s * entries=map->items.value ? (const %s *)((const uint8_t *)(const void *)&map->items+map->items.value) : NULL;\n if(entries==NULL) { return NULL; }\n while(lo0) { hi=mid; } else { return (%s *)(uintptr_t)(entries+mid); } }\n } return NULL;\n}\n", n, n, g.sym(n, "key_compare"), g.sym(n, "key"), n) + g.pf("static SCHEMA_UNUSED %s * %s(TableWorker * worker,TableMap * map,%s key)\n{\n TableCtx ctx; %s * entry;\n if(worker==NULL || worker->arena==NULL || worker->arena->locked) { return NULL; } ctx.arena=worker->arena;\n", n, g.sym(n, "place"), ktype, n) + if stringKey { + g.pf(" if(key.length<0 || key.length>%d || (key.length && key.data==NULL)) { return NULL; }\n", key.Type.Size) + } + g.pf(" entry=%s(&ctx,map,key);\n if(entry!=NULL) {\n", g.sym(n, "find")) + // Reset just the value: string keys may point into this same entry. + g.pf(" %s * value=entry; (void)value;\n", n) + g.emitTableResetField(value) + g.pf(" return entry;\n }\n entry=(%s *)table_sequence_add(worker,map,sizeof(%s)); if(entry==NULL) { return NULL; }\n %s(entry);\n", n, n, g.api(n, "reset")) + if stringKey { + g.pf(" if(key.length) { memcpy(entry->key,key.data,(size_t)key.length); } entry->key[key.length]=0; entry->key_length=key.length;\n") + } else { + g.pf(" entry->key=key;\n") + } + g.pf(" return entry;\n}\n") + keyParam, keyArg := ktype, "key" + if stringKey { + keyParam = "const char *" + keyArg = fmt.Sprintf("table_map_key(key,%d)", key.Type.Size) + } + handle := "&entry->value" + if mapPair(f) { + handle = "entry" + } + g.pf("static SCHEMA_UNUSED %s * %s(TableWorker * worker,TableMap * map,%s key)\n{ %s * entry=%s(worker,map,%s); return entry ? %s : NULL; }\n", vtype, g.api(surface, "insert"), keyParam, n, g.sym(n, "place"), keyArg, handle) + g.pf("static SCHEMA_UNUSED %s * %s(TableArena * arena,TableMap * map,%s key)\n{ TableCtx ctx; %s * entry; ctx.arena=arena; if(arena==NULL || arena->locked) { return NULL; } entry=%s(&ctx,map,%s); return entry ? %s : NULL; }\n", vtype, g.api(surface, "find_mut"), keyParam, n, g.sym(n, "find"), keyArg, handle) + result := "const " + vtype + " *" + found := handle + if value.Type.Pointer && value.Array == ir.ArrayNone { + target := value.Type.Name + if value.Type.Blob() { + target = "TableBlob" + } + result = "const " + target + " *" + found = "(" + result + ")table_ref_at(NULL,&entry->value)" + } + g.pf("static SCHEMA_UNUSED %s %s(const TableMap * map,%s key)\n{ const %s * entry=%s(NULL,map,%s); return entry ? %s : NULL; }\n", result, g.api(surface, "find"), keyParam, n, g.sym(n, "find"), keyArg, found) + g.pf("static SCHEMA_UNUSED int %s(TableArena * arena,TableMap * map,%s key)\n{ TableCtx ctx; %s * entry; ctx.arena=arena; if(arena==NULL || arena->locked) { return 0; } entry=%s(&ctx,map,%s); return entry ? table_sequence_erase(arena,map,entry,sizeof(%s)) : 0; }\n", g.api(surface, "erase"), keyParam, n, g.sym(n, "find"), keyArg, n) + g.pf("static SCHEMA_UNUSED TableSequenceCursor %s(const TableCtx * ctx,const TableMap * map)\n{ return table_sequence_cursor(ctx,map,sizeof(%s)); }\n", g.api(surface, "each"), n) + g.pf("static SCHEMA_UNUSED void * %s(void * worker,void * slot,const char * text,int32_t length,int64_t number)\n{\n", g.sym(n, "json_place")) + if stringKey { + g.pf(" TableMapKey key={text,length}; (void)number; return %s((TableWorker *)worker,(TableMap *)slot,key);\n", g.sym(n, "place")) + } else { + g.pf(" (void)text; (void)length; return %s((TableWorker *)worker,(TableMap *)slot,(%s)number);\n", g.sym(n, "place"), ktype) + } + g.pf("}\n") + g.emitMapKeyRead(f) + g.emitMessageMapKeyRead(f) + g.emitMapIndex(owner, f, result, found, keyParam, keyArg) +} + +func (g *tableGen) emitMapKeyRead(f *ir.Field) { + n := f.MapEntry.Name + readType := g.sym(n, "key_read") + key := ir.MapKeyField(f) + kt := g.mapKeyType(f) + kind := ir.TableWireScalarKind(key) + g.pf("typedef struct %s { %s key; int kind_bad,widened,over,malformed; } %s;\n", readType, kt, readType) + g.pf("static SCHEMA_UNUSED %s %s(TableReader r)\n{\n %s out; memset(&out,0,sizeof(out));\n for(;;) { uint64_t ref,id; uint8_t kind;\n if(!table_reader_leb(&r,&ref)) { out.malformed=1; return out; } if(ref==0) { out.malformed=r.offset!=r.size; return out; }\n if(ref>r.id_count || !table_reader_has(&r,1)) { out.malformed=1; return out; }\n id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r);\n if(id==UINT64_C(0x%016x)) {\n", readType, g.sym(n, "read_key"), readType, ir.MapKeyWireId) + g.pf(" if(kind!=%d && table_kind_widens(kind,%d)) { out.widened=1; } else { out.kind_bad=kind!=%d; }\n", kind, kind, kind) + g.pf(" if(!out.kind_bad) {\n") + if key.Type.Kind == ir.TString { + g.pf(" TableReader text; if(!table_reader_span(&r,&text) || !table_wire_utf8(text.buffer,text.size)) { out.malformed=1; return out; }\n out.key.data=(const char *)text.buffer; out.key.length=(int32_t)text.size; out.over=text.size>%d;\n", key.Type.Size) + } else { + g.wireScalarRead(key, "out.key", "r", "kind", " ", "out.malformed=1; return out;") + } + g.pf(" continue;\n }\n }\n if(!table_reader_skip(&r,kind)) { out.malformed=1; return out; }\n }\n}\n") +} + +func (g *tableGen) emitMapRead(f *ir.Field, dst, bounded string) { + n := f.MapEntry.Name + g.pf(" TableReader sub;\n") + if bounded == "" { + g.pf(" if(!table_reader_span(r,&sub)) { r->report->malformed=1; return 0; }\n") + } else { + g.pf(" sub=%s; %s.offset=%s.size;\n", bounded, bounded, bounded) + } + g.pf(" if(sub.size>=2) {\n uint8_t elem_kind; uint64_t count,i; TableSequenceFill fill; %s * last=NULL; %s previous; int widened=0; memset(&previous,0,sizeof(previous));\n", n, g.sym(n, "key_read")) + if bounded == "" { + g.pf(" if(!table_reader_array_header(&sub,r,&elem_kind,&count)) { r->report->malformed=1; break; }\n") + } else { + g.pf(" elem_kind=table_reader_get8(&sub); if(!table_reader_leb(&sub,&count)) { r->report->malformed=1; break; }\n") + } + g.pf(" if(elem_kind!=13) { r->report->kind_mismatch++; break; }\n") + g.retainReplace(f, " ") + g.pf(" fill=table_sequence_fill(r->nodes ? r->nodes->sink : NULL,&%s,count,sizeof(%s),SCHEMA_TABLE_ALIGNOF(%s));\n if(fill.refused) { r->nodes->refused=1; return 0; }\n if(!fill.ok) { r->report->malformed=1; break; }\n", dst, n, n) + g.pf(" for(i=0;ireport->malformed=1; break; }\n key=%s(item);\n if(key.widened && !widened) { widened=1; r->report->widened++; }\n if(key.kind_bad) { r->report->kind_mismatch++; memset(&%s,0,sizeof(%s)); break; }\n if(key.malformed) { r->report->malformed=1; break; }\n if(key.over) { r->report->clamped++; continue; }\n order=last ? %s(previous.key,key.key) : -1;\n if(order>0) { r->report->malformed=1; break; }\n if(order==0) { entry=last; r->report->duplicate++; }\n else { entry=(%s *)table_sequence_fill_next(&fill); }\n if(entry==NULL) { r->report->malformed=1; break; }\n %s%s(&item,entry); if(r->nodes!=NULL && r->nodes->refused) { return 0; } last=entry; previous=key;\n }\n table_sequence_fill_end(&fill);\n }\n", g.sym(n, "key_read"), n, g.sym(n, "read_key"), dst, dst, g.sym(n, "key_compare"), n, g.retainMapIndex(), g.api(n, "load_body")) +} + +func (g *tableGen) emitMapIndex(owner *ir.Struct, f *ir.Field, result, found, keyParam, keyArg string) { + n := f.MapEntry.Name + surface := owner.Name + ir.GoExportName(f.Name) + stringKey := ir.MapKeyField(f).Type.Kind == ir.TString + g.pf("static SCHEMA_UNUSED uint64_t %s(%s key)\n{ return ", g.sym(n, "hash"), g.mapKeyType(f)) + if stringKey { + g.pf("key.length>0 ? table_map_hash(key.data,key.length) : table_map_hash(NULL,0)") + } else { + g.pf("table_map_hash(&key,sizeof(key))") + } + g.pf("; }\n") + g.pf("static SCHEMA_UNUSED int64_t %s(const TableMap * map)\n{ int32_t slots=table_map_index_slots(map->count); return slots ? (int64_t)slots*(int64_t)sizeof(int32_t) : -1; }\n", g.api(surface, "index_measure")) + g.pf("static SCHEMA_UNUSED TableMapIndex %s(const TableMap * map,void * storage,int64_t bytes)\n{\n TableMapIndex index={NULL,0,0}; int32_t i,slots=table_map_index_slots(map->count); const %s * entries;\n if(!slots || storage==NULL || ((uintptr_t)storage & (SCHEMA_TABLE_ALIGNOF(int32_t)-1)) || bytes<(int64_t)slots*(int64_t)sizeof(int32_t)) { return index; }\n index.slots=(int32_t *)storage; index.capacity=slots; memset(storage,0,(size_t)slots*sizeof(int32_t));\n entries=map->items.value ? (const %s *)((const uint8_t *)(const void *)&map->items+map->items.value) : NULL;\n if(map->count && entries==NULL) { return index; }\n for(i=0;icount;i++) { int32_t at=(int32_t)(%s(%s(entries+i))&(uint64_t)(slots-1)); while(index.slots[at]) { at=(at+1)&(slots-1); } index.slots[at]=i+1; }\n index.good=1; return index;\n}\n", g.api(surface, "index"), n, n, g.sym(n, "hash"), g.sym(n, "key")) + g.pf("static SCHEMA_UNUSED %s %s(const TableMapIndex * index,const TableMap * map,%s key)\n{\n int32_t at,probe; %s lookup=%s; const %s * entries;\n if(index==NULL || !index->good) { return %s(map,key); }\n entries=map->items.value ? (const %s *)((const uint8_t *)(const void *)&map->items+map->items.value) : NULL; if(entries==NULL) { return NULL; }\n at=(int32_t)(%s(lookup)&(uint64_t)(index->capacity-1));\n for(probe=0;probecapacity;probe++) { int32_t slot=index->slots[at]; const %s * entry;\n if(slot<=0 || slot>map->count) { return NULL; } entry=entries+slot-1;\n if(%s(%s(entry),lookup)==0) { return %s; } at=(at+1)&(index->capacity-1);\n } return NULL;\n}\n", result, g.api(surface, "index_find"), keyParam, g.mapKeyType(f), keyArg, n, g.api(surface, "find"), n, g.sym(n, "hash"), n, g.sym(n, "key_compare"), g.sym(n, "key"), found) +} + +func (g *tableGen) retainMapIndex() string { + if g.retain { + return "retention=table_retain_index(retention,(uint32_t)(fill.value->count-1));" + } + return "" +} diff --git a/internal/codegen/ctable/message.go b/internal/codegen/ctable/message.go new file mode 100644 index 000000000..022f0bb06 --- /dev/null +++ b/internal/codegen/ctable/message.go @@ -0,0 +1,329 @@ +package ctable + +import ( + "fmt" + "strings" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +func tableMessageForm(u *ir.Unit, variable bool) string { + var b strings.Builder + b.WriteString("#ifndef SCHEMA_" + strings.ToUpper(u.Package) + "_TABLE_MESSAGE\n#define SCHEMA_" + strings.ToUpper(u.Package) + "_TABLE_MESSAGE\n") + entries, announcement := ir.TableVocabulary(u), ir.TableAnnouncement(u) + fmt.Fprintf(&b, "enum { kTableMessageRefBitsHere=%d, kTableMessageEntriesHere=%d, kTableAnnounceBytes=%d, kTableMessageBatchMax=256 };\n", ir.TableMessageRefBits(len(entries)), len(entries), len(announcement)) + b.WriteString("static const uint8_t kTableAnnounce[kTableAnnounceBytes]={") + for _, v := range announcement { + fmt.Fprintf(&b, "0x%02x,", v) + } + b.WriteString("};\n") + nodes := "" + if variable { + nodes = "TableNumbering * nodes; int64_t index_bits;" + } + b.WriteString(strings.ReplaceAll(cMessageBits, "@NODES@", nodes)) + b.WriteString(cMessageNumbers) + b.WriteString(cMessageShape) + b.WriteString(cMessageAnnounce) + readNodes := "" + if variable { + readNodes = "TableNodeMap * nodes;" + } + b.WriteString(strings.ReplaceAll(cMessageRead, "@NODES@", readNodes)) + b.WriteString(cMessageNodes) + b.WriteString("\n#endif\n") + return b.String() +} + +// A null output performs exactly the same walk as a save, counting bits. +// Reads and writes touch only the bytes occupied by the requested value. +const cMessageBits = ` +typedef struct TableBitWriter { uint8_t * buffer; int64_t capacity,bits; int overflow,check_default; @NODES@ } TableBitWriter; +typedef struct TableBitReader { const uint8_t * buffer; int64_t bits,offset; } TableBitReader; +static SCHEMA_UNUSED TableBitWriter table_bit_writer(uint8_t * buffer,int64_t capacity) +{ TableBitWriter w;memset(&w,0,sizeof(w));w.buffer=buffer;w.capacity=capacity;return w; } +static SCHEMA_UNUSED TableBitReader table_bit_reader(const uint8_t * buffer,int64_t bytes) +{ TableBitReader r={buffer,bytes>=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i=size)return 0;f->packing=in[(*at)++];if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0;if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i);hi|=(uint64_t)in[*at+8+i]<<(8*i);} + f->base_lo=(int64_t)lo;f->base_hi=(int64_t)hi;*at+=16;return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v;return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0};if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12;memcpy(&f->qmin,raw,4);memcpy(&f->qmax,raw+1,4);memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0;f->max=(int64_t)v;return 1;} + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0;f->min=(int64_t)v;} + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0;f->max=(int64_t)v; + if(*at>=size)return 0;f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0};int i; + if(*at<0||*at>size-9)return 0;memset(e,0,sizeof(*e));e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i);e->kind=in[*at+8];*at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing;e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min;e->max=own.max;e->base_lo=own.base_lo;e->base_hi=own.base_hi;e->qmin=own.qmin;e->qdelta=own.qdelta;e->qcount=own.qcount;e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing;e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max;e->elem_base_lo=elem.base_lo;e->elem_base_hi=elem.base_hi;e->elem_qmin=elem.qmin;e->elem_qdelta=elem.qdelta;e->elem_qcount=elem.qcount; + }return 1; +} + +` + +const cMessageAnnounce = ` +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r;int seen_version=0,seen_words=0;const uint8_t * words=NULL;int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id;uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref);kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r);seen_version++;continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field;uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1;report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE;return 0;} + words=field.buffer+field.offset;words_bytes=(int64_t)n;seen_words++;continue; + } + report->unknown++;if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1;report->reason=SCHEMA_TABLE_NO_VOCABULARY;goto refused;} + if(count>=v->max_entries){report->refused=1;report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE;goto refused;} + entry=&v->entries[count];touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed;} + entry->min=began;entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0;{int64_t i;for(i=0;ientries+i))goto malformed;} + v->count=count;v->ref_bits=table_bits_required(0,count);v->announced=1;return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries));return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0};int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1;report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT;return 0;} + ok=table_announce_once(v,buffer,bytes,report);if(!ok)v->refused=1;return ok; +} +` + +const cMessageNumbers = `static SCHEMA_UNUSED int64_t table_bits_required( int64_t min, int64_t max ) +{ + if ( max <= min ) { return 0; } + uint64_t span = (uint64_t) max - (uint64_t) min; + int64_t n = 0; + while ( span > 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); // floor of a non-negative value + if ( index > count ) { index = count; } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + +` diff --git a/internal/codegen/ctable/message_extent.go b/internal/codegen/ctable/message_extent.go new file mode 100644 index 000000000..1201177fd --- /dev/null +++ b/internal/codegen/ctable/message_extent.go @@ -0,0 +1,120 @@ +package ctable + +import ( + "fmt" + "slices" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +func (g *tableGen) emitMessageExtentDeclarations(members []*ir.Struct, unions []*ir.Union) { + for _, st := range members { + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader *,int64_t *);\n", g.sym(st.Name, "message_extent")) + } + for _, un := range unions { + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader *,int64_t *);\n", g.sym(un.Name, "message_extent")) + } +} + +func (g *tableGen) emitMessageExtent(st *ir.Struct) { + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader * r,int64_t * at)\n{\n (void)at;for(;;){const TableMessageEntry * entry;if(!table_message_ref(r,&entry,0))return 0;if(entry==NULL)return 1;\n switch(entry->id){\n", g.sym(st.Name, "message_extent")) + step := 0 + for _, f := range st.Fields { + if !g.messageFieldHasExtent(f) { + continue + } + e := ir.TableFieldEntry(f) + g.pf(" case UINT64_C(0x%016x):if(entry->kind==%d && (entry->elem_kind==%d || table_kind_widens(entry->elem_kind,%d))){\n", e.Id, e.Kind, e.Shape.Elem, e.Shape.Elem) + g.messageExtentValue(f, "entry", " ", &step) + g.pf(" continue;}break;\n") + } + g.pf(" default:break;}if(!table_message_skip(r,entry))return 0;\n }\n}\n") +} + +func (g *tableGen) emitMessageUnionExtent(un *ir.Union) { + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader * r,int64_t * at)\n{\n const TableMessageEntry * entry;(void)at;if(!table_message_ref(r,&entry,0))return 0;if(entry==NULL)return 1;if(entry->kind==0)return 0;\n switch(entry->id){\n", g.sym(un.Name, "message_extent")) + step := 0 + for _, v := range un.Variants { + if v.Void() || !g.messageFieldHasExtent(v.F) { + continue + } + e := ir.TableArmEntry(v) + g.pf(" case UINT64_C(0x%016x):if(entry->kind==%d && (entry->elem_kind==%d || table_kind_widens(entry->elem_kind,%d))){\n", e.Id, e.Kind, e.Shape.Elem, e.Shape.Elem) + g.messageExtentValue(v.F, "entry", " ", &step) + g.pf(" return 1;}break;\n") + } + g.pf(" default:break;}return table_message_skip(r,entry);\n}\n") +} + +func (g *tableGen) messageExtentValue(f *ir.Field, e, ind string, step *int) { + *step++ + label := fmt.Sprintf("extent%d", *step) + if f.IsList() || f.IsMap() || f.Array != ir.ArrayNone { + g.pf("%s{uint64_t %s_n,%s_i;TableMessageEntry %s_shape=table_message_element(%s);const TableMessageEntry * %s=&%s_shape;\n", ind, label, label, label, e, label, label) + g.pf("%s if(!table_message_count(r,%s,&%s_n))return 0;\n", ind, e, label) + elem := f + if f.IsList() || f.IsMap() { + elem = sequenceElement(f) + g.pf("%s if(%s_n>INT32_MAX || !table_extent_reserve(at,(int64_t)%s_n,sizeof(%s),SCHEMA_TABLE_ALIGNOF(%s))){r->extent_refused=1;return 0;}\n", ind, label, label, g.sequenceType(f), g.sequenceType(f)) + } + if !g.messageFieldHasExtent(elem) || f.Type.Pointer { + g.pf("%s if(%s->value_bits>=0 && %s->kind!=16){if(!table_message_skip_run(r,%s_n,%s->value_bits))return 0;}else{\n", ind, label, e, label, label) + } else { + g.pf("%s {\n", ind) + } + g.pf("%s for(%s_i=0;%s_i<%s_n;%s_i++){\n", ind, label, label, label, label) + condition := "1" + if f.KeyEnum != "" { + g.pf("%s const TableMessageEntry * %s_key;int %s_known=0;if(!table_message_ref(r,&%s_key,1)||%s_key==NULL)return 0;switch(%s_key->id){\n", ind, label, label, label, label, label) + for i := range f.KeyEnumRef.Variants { + g.pf("%s case UINT64_C(0x%016x):%s_known=1;break;\n", ind, ir.TableWireId(f.KeyEnumRef.VariantWireName(i)), label) + } + g.pf("%s default:break;}\n", ind) + condition = label + "_known" + } else if !f.IsList() && !f.IsMap() { + condition = fmt.Sprintf("%s_i<%d", label, f.ArrayBound) + } + g.pf("%s if(%s){\n", ind, condition) + g.messageExtentElement(elem, label, ind+" ") + g.pf("%s }else if(!table_message_skip(r,%s))return 0;\n%s } } }\n", ind, label, ind) + return + } + g.messageExtentElement(f, e, ind) +} + +func (g *tableGen) messageExtentElement(f *ir.Field, e, ind string) { + if !f.Type.Pointer { + switch ref := f.Type.Ref.(type) { + case *ir.Struct: + g.pf("%sif(!%s(r,at))return 0;\n", ind, g.sym(ref.Name, "message_extent")) + return + case *ir.Union: + g.pf("%sif(!%s(r,at))return 0;\n", ind, g.sym(ref.Name, "message_extent")) + return + } + } + g.pf("%sif(!table_message_skip(r,%s))return 0;\n", ind, e) +} + +func (g *tableGen) messageHasExtent(st *ir.Struct) bool { + return slices.ContainsFunc(st.Fields, g.messageFieldHasExtent) +} +func (g *tableGen) messageFieldHasExtent(f *ir.Field) bool { + if f.IsMap() || f.IsList() { + return true + } + if f.Type.Pointer { + return false + } + switch ref := f.Type.Ref.(type) { + case *ir.Struct: + return g.messageHasExtent(ref) + case *ir.Union: + for _, v := range ref.Variants { + if !v.Void() && g.messageFieldHasExtent(v.F) { + return true + } + } + } + return false +} diff --git a/internal/codegen/ctable/message_graph.go b/internal/codegen/ctable/message_graph.go new file mode 100644 index 000000000..3759b0249 --- /dev/null +++ b/internal/codegen/ctable/message_graph.go @@ -0,0 +1,134 @@ +package ctable + +import "fmt" + +import "github.com/mas-bandwidth/schema/v2/ir" + +const cMessageNodes = ` +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref;int64_t start=r->bits.offset;*count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start;r->index_bits=1;return 1;} + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} +` + +func (g *tableGen) emitMessageGraph(st *ir.Struct) { + if st.IsMapEntry() { + return + } + g.emitMessageGraphSave(st) + g.emitMessageGraphScan(st) + g.emitMessageGraphLoad(st) +} + +func (g *tableGen) emitMessageGraphSave(st *ir.Struct) { + n := st.Name + g.pf("static SCHEMA_UNUSED int %s(TableBitWriter * w,const %s * root,TableAllocator allocator)\n{\n TableNumbering numbering;uint64_t i;int ok=0;\n if(!table_number_build(&numbering,NULL,root,&%s,allocator))goto done;\n w->nodes=&numbering;w->index_bits=table_bits_required(0,(int64_t)numbering.count);\n if(numbering.count-1>UINT32_MAX)goto done;\n if(numbering.count>1){\n", g.sym(n, "message_graph_save"), n, g.graphType(n)) + g.messageHeader(ir.TableVocabularyEntry{Id: ir.TableNodeWireId}, " ") + g.pf(" table_bit_put(w,numbering.count-1,32);for(i=1;itype->id){\n") + bytesBlob, stringBlob := ir.PointerReachableBlobs(st) + for _, b := range []struct { + yes bool + id uint64 + }{{bytesBlob, ir.BytesWireTypeId}, {stringBlob, ir.StringWireTypeId}} { + if !b.yes { + continue + } + g.pf(" case UINT64_C(0x%016x): {const TableBlob * blob=(const TableBlob *)node->value;\n", b.id) + g.messageHeader(ir.TableVocabularyEntry{Id: b.id}, " ") + g.pf(" table_bit_put(w,blob->length,32);table_bit_align_write(w);table_bit_putbytes(w,blob+1,blob->length);break;}\n") + } + for _, t := range ir.PointerReachable(st) { + g.pf(" case UINT64_C(0x%016x):\n", ir.TableWireId(t.WireName())) + g.messageHeader(ir.TableVocabularyEntry{Id: ir.TableWireId(t.WireName())}, " ") + g.pf(" if(!%s(w,(const %s *)node->value))goto done;break;\n", g.api(t.Name, "save_message_body"), t.Name) + } + g.pf(" default:goto done;\n } } }ok=%s(w,root);\n done:table_number_dispose(&numbering);w->nodes=NULL;return ok&&!w->overflow;\n}\n", g.api(n, "save_message_body")) + for _, measure := range []bool{true, false} { + verb, extra, buffer, capacity := "save_messages", ",uint8_t * buffer,int64_t capacity", "buffer", "capacity" + if measure { + verb, extra, buffer, capacity = "measure_messages", "", "NULL", "INT64_MAX" + } + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * const * roots,int64_t count%s,TableReport * report,TableAllocator allocator)\n{\n TableBitWriter w=table_bit_writer(%s,%s);int64_t i;\n if(roots==NULL||count<1)return -1;if(count>kTableMessageBatchMax){if(report){report->refused=1;report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE;}return -1;}\n", g.api(n, verb+"_with_allocator"), n, extra, buffer, capacity) + if !measure { + g.pf(" if(buffer==NULL||capacity<0)return -1;\n") + } + g.pf(" table_bit_put(&w,2,8);table_bit_put(&w,(uint64_t)(count-1),8);for(i=0;iid;\n if(*id==UINT64_C(0x%016x)||*id==UINT64_C(0x%016x)){\n if(!table_message_blob_scan(r,length))return 0;\n", g.sym(n, "message_record_scan"), ir.BytesWireTypeId, ir.StringWireTypeId) + if bytesBlob { + g.pf(" if(*id==UINT64_C(0x%016x))*size=table_blob_storage((int64_t)*length,0);\n", ir.BytesWireTypeId) + } + if stringBlob { + g.pf(" if(*id==UINT64_C(0x%016x))*size=table_blob_storage((int64_t)*length,1);\n", ir.StringWireTypeId) + } + g.pf(" return *size>=0; }switch(*id){\n") + for _, t := range ir.PointerReachable(st) { + g.pf(" case UINT64_C(0x%016x):at=sizeof(%s);if(!%s(r,&at))return 0;break;\n", ir.TableWireId(t.WireName()), t.Name, g.sym(t.Name, "message_extent")) + } + g.pf(" default:return table_message_skip_body(r);\n }if(at>INT64_MAX-kTableAlign)return 0;*size=table_align_up64(at);return 1;\n}\n") + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader * r,uint64_t * records,int64_t * data,int64_t * root_bytes,int * complete)\n{\n uint64_t i,id,length;int64_t size,root=sizeof(%s);*data=0;*complete=1;\n if(!table_message_nodes_open(r,records))return 0;for(i=0;i<*records;i++){\n if(!%s(r,&id,&size,&length)||size>INT64_MAX-*data)return 0;*data+=size;}\n if(!%s(r,&root))*complete=0;if(r->extent_refused)return 0;if(root>INT64_MAX-kTableAlign)return 0;*root_bytes=table_align_up64(root);if(*root_bytes>INT64_MAX-*data)return 0;*data+=*root_bytes;return 1;\n}\n", g.sym(n, "message_storage"), n, g.sym(n, "message_record_scan"), g.sym(n, "message_extent")) + g.pf("static SCHEMA_UNUSED int64_t %s(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,int64_t * attribution,TableRefuseReason * reason)\n{\n TableReport report={0};TableMessageReader r;int64_t total=0,dirs=0,bodies,i;if(attribution)*attribution=0;\n bodies=table_message_open(&r,vocabulary,buffer,bytes,&report);if(bodies<0)return -1;\n for(i=0;i=(uint64_t)(INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)))return -1;dir=(int64_t)(records+1)*(int64_t)sizeof(TableNodeDirEntry);\n if(data>INT64_MAX-dir || data+dir>INT64_MAX-total)return -1;total+=data+dir;dirs+=dir;if(!complete)break;\n }if(attribution)*attribution=dirs;return total;\n}\n", g.api(n, "load_measure_messages_ex"), g.sym(n, "message_storage")) + g.pf("static SCHEMA_UNUSED int64_t %s(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes)\n{ return %s(vocabulary,buffer,bytes,NULL,NULL); }\n", g.api(n, "load_measure_messages"), g.api(n, "load_measure_messages_ex")) +} + +func (g *tableGen) emitMessageGraphLoad(st *ir.Struct) { + n := st.Name + _, stringBlob := ir.PointerReachableBlobs(st) + rootScan := "" + if g.messageHasExtent(st) { + rootScan = fmt.Sprintf("if(!%s(&scan,&root_size))goto malformed;", g.sym(n, "message_extent")) + } + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const %s ** out)\n{\n uint64_t count,i,id,length;int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(%s);\n TableNodeDirEntry * directory;TableNodeMap nodes;TableRegionSink carve;TableSink sink;TableMessageReader scan;%s * root;int ok;\n if(!table_message_nodes_open(r,&count))goto malformed;dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry);\n if(*used>capacity||dir_bytes>capacity-*used)goto malformed;directory=(TableNodeDirEntry *)(void *)(region+*used);*used+=dir_bytes;\n memset(&nodes,0,sizeof(nodes));nodes.base=region;nodes.entries=directory;nodes.count=count+1;records_start=r->bits.offset;\n for(i=0;icapacity-*used)goto malformed;directory[i+1].offset=(uint64_t)*used;\n if(id==UINT64_C(0x%016x)||id==UINT64_C(0x%016x)){TableBlob * blob=(TableBlob *)(void *)(region+*used);blob->length=(uint32_t)length;blob->zero=0;}*used+=size;\n }\n fields_start=r->bits.offset;scan=*r;(void)scan;%sif(root_size>INT64_MAX-kTableAlign)goto malformed;root_size=table_align_up64(root_size);\n if(root_size>capacity-*used)goto malformed;root=(%s *)(void *)(region+*used);directory[0].offset=(uint64_t)*used;directory[0].type_id=UINT64_C(0x%016x);*used+=root_size;%s(root);*out=root;nodes.good=1;r->nodes=&nodes;\n sink.worker=NULL;sink.region=&carve;nodes.sink=&sink;r->bits.offset=records_start;\n", g.sym(n, "message_load_into"), n, n, n, g.sym(n, "message_record_scan"), ir.BytesWireTypeId, ir.StringWireTypeId, rootScan, n, ir.TableWireId(st.WireName()), g.api(n, "reset")) + if g.retain { + g.pf(" table_retain_reset(retention.retain,&nodes,region);\n") + } + g.pf(" for(i=0;iid;\n if(id==UINT64_C(0x%016x)||id==UINT64_C(0x%016x)){\n if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed;\n", ir.BytesWireTypeId, ir.StringWireTypeId) + if stringBlob { + g.pf(" if(id==UINT64_C(0x%016x)&&!table_wire_utf8(r->bits.buffer+(r->bits.offset>>3),length))goto malformed;\n", ir.StringWireTypeId) + } + g.pf(" if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length);r->bits.offset+=(int64_t)length*8;continue;\n }\n if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed;continue;}\n value=region+directory[i+1].offset;carve.base=value;switch(id){\n") + for _, t := range ir.PointerReachable(st) { + if g.retain { + g.pf(" case UINT64_C(0x%016x):retention.path=table_retain_root(value,(uint32_t)i+2);\n", ir.TableWireId(t.WireName())) + } + prefix := "case UINT64_C(0x%016x):" + if g.retain { + prefix = "/* type 0x%016x */" + } + g.pf(" "+prefix+"carve.used=sizeof(%s);carve.capacity=sizeof(%s);scan=*r;if(!%s(&scan,&carve.capacity))goto malformed;if(!%s(r,(%s *)(void *)value))goto malformed;break;\n", ir.TableWireId(t.WireName()), t.Name, t.Name, g.sym(t.Name, "message_extent"), g.api(t.Name, "load_message_body"), t.Name) + } + g.pf(" default:goto malformed;}\n }if(r->bits.offset!=fields_start)goto malformed;%scarve.base=(uint8_t *)(void *)root;carve.used=sizeof(*root);carve.capacity=root_size;\n ok=%s(r,root);r->nodes=NULL;if(!ok)return 0;*out=root;return 1;\n malformed:r->nodes=NULL;r->report->malformed=1;return 0;\n}\n", g.retainMessageRoot(), g.api(n, "load_message_body")) + if g.retain { + return + } + g.pf("static SCHEMA_UNUSED int %s(const %s ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report)\n{\n TableReport ignored={0};TableMessageReader r;int64_t bodies,capacity,used=0,i;if(report==NULL)report=&ignored;\n if(roots==NULL||count==NULL){report->malformed=1;return 0;}capacity=*count;*count=0;bodies=table_message_open(&r,vocabulary,buffer,bytes,report);if(bodies<0)return 0;\n if(bodies>capacity){*count=bodies;report->refused=1;report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE;return 0;}\n if(region==NULL||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1;return 0;}memset(region,0,(size_t)region_bytes);\n for(i=0;ibits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v;memset(&v,0,sizeof(v));v.kind=e->elem_kind; + v.packing=e->elem_packing;v.value_bits=e->elem_value_bits;v.base_lo=e->elem_base_lo;v.base_hi=e->elem_base_hi; + v.max=e->elem_max;v.qmin=e->elem_qmin;v.qdelta=e->elem_qdelta;v.qcount=e->elem_qcount;return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i;const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits;uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo;*lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r));r->report=report;r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1;return -1;} + if(buffer[0]!=2){report->refused=1;report->reason=SCHEMA_TABLE_NEWER_FORM;return -1;} + if(v==NULL || !v->announced){report->refused=1;report->reason=SCHEMA_TABLE_NO_VOCABULARY;return -1;} + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1;return -1;} + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1;return 0;}return 1; +} +` + +func (g *tableGen) messageReadEnum(e *ir.Enum, dst, ind string) { + g.pf("%s{ const TableMessageEntry * named; if(!table_message_ref(r,&named,1)) goto malformed;\n%s if(named==NULL) %s=%s; else switch(named->id) {\n", ind, ind, dst, enumNoneConst(e.Name)) + for i, v := range e.Variants { + g.pf("%s case UINT64_C(0x%016x): %s=%s;break;\n", ind, ir.TableWireId(e.VariantWireName(i)), dst, enumConst(e.Name, v)) + } + g.pf("%s default:%s=%s;"+g.unknownEvent()+"break;\n%s } }\n", ind, dst, enumNoneConst(e.Name), ind) +} + +func (g *tableGen) messageReadScalar(f *ir.Field, dst, e, ind string) { + if en := enumRef(f); en != nil { + g.messageReadEnum(en, dst, ind) + return + } + kind := ir.TableWireScalarKind(f) + if kind == tkF32 || kind == tkF64 { + typ := "float" + if kind == tkF64 { + typ = "double" + } + g.pf("%s{ %s decoded; if(%s->kind==10) {float narrow;if(!table_message_float(r,%s,&narrow))goto malformed;", ind, typ, e, e) + if kind == tkF64 { + g.pf("decoded=table_wire_widen_f32(table_float_to_bits(narrow));") + } else { + g.pf("decoded=narrow;") + } + g.pf("} else {uint64_t raw;if(!table_bit_get(&r->bits,&raw,64))goto malformed;decoded=(%s)table_bits_to_double(raw);}\n", typ) + if f.HasFloatRange { + g.pf("%s if(decoded<%s){decoded=%s;r->report->clamped++;}else if(decoded>%s){decoded=%s;r->report->clamped++;}\n", ind, formatFloat(f.FMin, kind == tkF32), formatFloat(f.FMin, kind == tkF32), formatFloat(f.FMax, kind == tkF32), formatFloat(f.FMax, kind == tkF32)) + } + g.pf("%s %s=decoded; }\n", ind, dst) + return + } + g.pf("%s{ uint64_t lo,hi;if(!table_message_number(r,%s,&lo,&hi))goto malformed;\n", ind, e) + if f.Type.Width == 128 { + typ := g.cFieldType(f.Type) + g.pf("%s %s decoded;decoded.lo=lo;decoded.hi=hi;\n", ind, typ) + if low, high, ok := ir.TableRawRange(f); ok { + for i, b := range []*big.Int{low, high} { + op := "<" + if i == 1 { + op = ">" + } + cmp := fmt.Sprintf("decoded.hi%sbound.hi || (decoded.hi==bound.hi && decoded.lo%sbound.lo)", op, op) + if f.Type.Signed { + cmp = "serialize_int128_compare(decoded,bound)" + op + "0" + } + g.pf("%s { %s bound=%s;if(%s){decoded=bound;r->report->clamped++;} }\n", ind, typ, tableWideLit(b, f.Type.Signed), cmp) + } + } + g.pf("%s %s=decoded; }\n", ind, dst) + return + } + if kind == tkBool { + g.pf("%s %s=lo!=0; }\n", ind, dst) + return + } + signed := ir.TableKindSigned(kind) + typ := "uint64_t" + if signed { + typ = "int64_t" + } + g.pf("%s %s decoded=(%s)lo;(void)hi;\n", ind, typ, typ) + bits := tableKindWidth(kind) * 8 + low, high := messageRange(signed, bits) + if a, b, ok := ir.TableRawRange(f); ok { + if a.Cmp(low) > 0 { + low = a + } + if b.Cmp(high) < 0 { + high = b + } + } + if f.Type.Kind == ir.TBits { + high = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), uint(f.Type.Width)), big.NewInt(1)) + } + floor, ceiling := messageRange(signed, 64) + if low.Cmp(floor) > 0 { + s := tableIntLit(low, signed, 8) + g.pf("%s if(decoded<%s){decoded=%s;r->report->clamped++;}\n", ind, s, s) + } + if high.Cmp(ceiling) < 0 { + s := tableIntLit(high, signed, 8) + g.pf("%s if(decoded>%s){decoded=%s;r->report->clamped++;}\n", ind, s, s) + } + g.pf("%s %s=(%s)decoded; }\n", ind, dst, g.cFieldType(f.Type)) +} + +func messageRange(signed bool, bits int) (*big.Int, *big.Int) { + if signed { + n := new(big.Int).Lsh(big.NewInt(1), uint(bits-1)) + return new(big.Int).Neg(n), new(big.Int).Sub(n, big.NewInt(1)) + } + return big.NewInt(0), new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), uint(bits)), big.NewInt(1)) +} + +func (g *tableGen) messageReadElement(f *ir.Field, dst, e, ind string) { + switch { + case f.Type.Pointer: + target := ir.TableWireId(ir.PointeeWireName(f)) + if f.Type.Blob() { + target = ir.BlobWireTypeId(f) + } + g.pf("%s{uint64_t index;if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed;table_node_resolve(r->nodes,&%s,index,UINT64_C(0x%016x),r->report);}\n", ind, dst, target) + case ir.TableWireScalarKind(f) == tkTable: + g.pf("%sif(!%s(r,&%s))return 0;\n", ind, g.api(f.Type.Name, "load_message_body"), dst) + case ir.TableWireScalarKind(f) == tkUnion: + g.pf("%sif(!%s(r,&%s))return 0;\n", ind, g.unionWireName(f.Type.Ref.(*ir.Union), "message_load"), dst) + default: + g.messageReadScalar(f, dst, e, ind) + } +} + +func (g *tableGen) messageReadValue(f *ir.Field, dst, count, e, ind string) { + switch { + case f.IsList() || f.IsMap(): + g.emitMessageSequenceRead(f, dst, e, ind) + case f.Type.Pointer && f.Array == ir.ArrayNone: + g.messageReadElement(f, dst, e, ind) + case f.Type.Kind == ir.TString || f.Type.Kind == ir.TBytes || f.Type.Kind == ir.TWString: + width := "table_bits_required(0," + e + "->max)" + bits := 8 + if f.Type.Kind == ir.TBytes { + width = "table_bits_required(" + e + "->min," + e + "->max)" + } + if f.Type.Kind == ir.TWString { + bits = 16 + } + g.pf("%s{uint64_t n,kept;if(!table_bit_get(&r->bits,&n,%s))goto malformed;\n", ind, width) + if bits == 8 { + g.pf("%s if(!table_bit_align_read(&r->bits))goto malformed;\n", ind) + } + g.pf("%s if(n>INT64_MAX/%d || !table_bit_has(&r->bits,(int64_t)n*%d))goto malformed;\n", ind, bits, bits) + if f.Type.Kind == ir.TString { + g.pf("%s {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3);if(!table_wire_utf8(text,n))goto malformed;kept=table_wire_utf8_clamp(text,n,%d);memcpy(%s,text,(size_t)kept);%s[kept]=0;}\n", ind, f.Type.Size, dst, dst) + g.pf("%s r->bits.offset+=(int64_t)n*8;\n", ind) + } else { + g.pf("%s kept=n>%d ? %d : n;\n", ind, f.Type.Size, f.Type.Size) + if bits == 8 { + g.pf("%s if(!table_bit_getbytes(&r->bits,%s,(int64_t)kept)||!table_message_skip_run(r,n-kept,8))goto malformed;\n", ind, dst) + } else { + g.pf("%s {uint64_t i,unit;int high=0,ill=0;for(i=0;ibits,&unit,16))goto malformed;\n", ind) + g.pf("%s if(unit==0)ill=1;if(high){if(unit<0xdc00||unit>0xdfff)ill=1;high=0;}else if(unit>=0xd800&&unit<=0xdbff)high=1;else if(unit>=0xdc00&&unit<=0xdfff)ill=1;\n", ind) + g.pf("%s if(i0 && kept=0xd800 && %s[kept-1]<=0xdbff)kept--; %s[kept]=0; }\n", ind, dst, dst, dst) + } + } + g.pf("%s if(keptreport->clamped++;%s=(int32_t)kept; }\n", ind, count) + case f.KeyEnum != "": + g.pf("%s{uint64_t n,i;TableMessageEntry element_shape=table_message_element(%s);const TableMessageEntry * element_entry=&element_shape;(void)element_entry;\n%s if(!table_message_count(r,%s,&n))goto malformed;for(i=0;iid){\n", ind, e, ind, e, ind) + for i, v := range f.KeyEnumRef.Variants { + g.pf("%s case UINT64_C(0x%016x):slot=%d;break;\n", ind, ir.TableWireId(f.KeyEnumRef.VariantWireName(i)), i) + _ = v + } + g.pf("%s default:break;} if(slot<0){"+g.unknownEvent()+"\n%s { %s scratch;memset(&scratch,0,sizeof(scratch));\n", ind, ind, g.sequenceType(f)) + if g.retain { + g.pf("%s TableRetainWalk named_retention=retention;retention.retain=NULL;\n", ind) + } + g.messageReadElement(f, "scratch", "element_entry", ind+" ") + if g.retain { + g.pf("%s retention=named_retention;\n", ind) + } + g.pf("%s }continue;}\n", ind) + g.retainIndex(f, "slot", ind+" ") + g.messageReadElement(f, dst+"[slot]", "element_entry", ind+" ") + g.pf("%s} }\n", ind) + case f.Array != ir.ArrayNone: + g.pf("%s{uint64_t n,kept,walk,i;TableMessageEntry element_shape=table_message_element(%s);const TableMessageEntry * element_entry=&element_shape;(void)element_entry;\n", ind, e) + g.pf("%s if(!table_message_count(r,%s,&n))goto malformed;kept=n>%d?%d:n;if(keptreport->clamped++;\n", ind, e, f.ArrayBound, f.ArrayBound) + g.retainReplace(f, ind+" ") + g.pf("%s walk=element_entry->value_bits>=0?kept:n;for(i=0;ivalue_bits))goto malformed;\n", ind) + if f.Array == ir.ArrayCounted { + g.pf("%s %s=(int32_t)kept;\n", ind, count) + } + g.pf("%s}\n", ind) + default: + g.messageReadElement(f, dst, e, ind) + } +} + +// A sender's shape may change while its kind remains compatible. The decode +// always uses that shape, then applies this declaration's bounds. +func (g *tableGen) messageAccept(f *ir.Field, e ir.TableVocabularyEntry, ind string) { + condition := fmt.Sprintf("entry->kind==%d && entry->elem_kind==%d", e.Kind, e.Shape.Elem) + widen := "0" + if f != nil && !f.Type.Pointer { + if f.Array != ir.ArrayNone && f.KeyEnum == "" && !f.IsMap() && enumRef(f) == nil { + widen = fmt.Sprintf("entry->kind==%d && table_kind_widens(entry->elem_kind,%d)", e.Kind, e.Shape.Elem) + } else if f.Array == ir.ArrayNone && !f.IsMap() && enumRef(f) == nil { + widen = fmt.Sprintf("entry->elem_kind==0 && table_kind_widens(entry->kind,%d)", e.Kind) + } + } + g.pf("%sif(!(%s)){if(%s){r->report->widened++;}else{r->report->kind_mismatch++;if(!table_message_skip(r,entry))goto malformed;break;}}\n", ind, condition, widen) +} + +func (g *tableGen) emitMessageReadDeclarations(members []*ir.Struct, unions []*ir.Union) { + for _, st := range members { + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader *,%s *);\n", g.api(st.Name, "load_message_body"), st.Name) + } + for _, un := range unions { + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader *,%s *);\n", g.unionWireName(un, "message_load"), un.Name) + } +} + +func (g *tableGen) emitMessageUnionRead(un *ir.Union) { + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader * r,%s * value)\n{\n const TableMessageEntry * entry;\n %sif(!table_message_ref(r,&entry,0))goto malformed;value->type=0;if(entry==NULL)return 1;if(entry->kind==0)goto malformed;\n switch(entry->id){\n", g.unionWireName(un, "message_load"), un.Name, g.retainUnionDiscard()) + for ordinal, v := range un.Variants { + g.pf(" case UINT64_C(0x%016x): {\n", ir.TableWireId(v.WireName())) + g.messageAccept(v.F, ir.TableArmEntry(v), " ") + if !v.Void() { + if g.retain { + g.pf(" retention=table_retain_step(retention,%d,0);\n", ordinal) + } + dst := armValue("(*value)", v) + count := dst + "_count" + if v.F.Array == ir.ArrayNone { + count = dst + "_length" + } + g.pf(" memset(&%s,0,sizeof(%s));\n", dst, dst) + g.messageReadValue(v.F, dst, count, "entry", " ") + } + g.pf(" value->type=%s;break;}\n", enumConst(un.Name+"Type", v.Name)) + } + g.pf("%s", " default:"+g.unknownEvent()+"if(!table_message_skip(r,entry))goto malformed;break;\n }return 1;\n malformed:r->report->malformed=1;return 0;\n}\n") +} + +func (g *tableGen) emitMessageRead(st *ir.Struct) { + g.pf("static SCHEMA_UNUSED int %s(TableMessageReader * r,%s * value)\n{\n", g.api(st.Name, "load_message_body"), st.Name) + if g.retain { + g.pf(" TableRetainWalk body_keep=retention;table_retain_discard(retention,0,0);\n") + } + g.pf(" %s(value);for(;;){const TableMessageEntry * entry;\n if(!table_message_ref(r,&entry,0))goto malformed;if(entry==NULL)return 1;switch(entry->id){\n", g.api(st.Name, "reset")) + for ordinal, f := range st.Fields { + g.pf(" case UINT64_C(0x%016x): {\n", ir.TableFieldWireId(f)) + g.messageAccept(f, ir.TableFieldEntry(f), " ") + g.retainReadField(f, ordinal, " ") + dst, count := "value->"+f.Name, "value->"+f.Name+"_count" + if f.Array == ir.ArrayNone { + count = "value->" + f.Name + "_length" + } + g.messageReadValue(f, dst, count, "entry", " ") + if f.Type.Optional { + g.pf(" %s_present=1;\n", dst) + } + g.pf(" break;}\n") + } + if g.retain { + g.pf(" default:r->report->unknown += 1;if(!table_retain_message_capture(r,entry,body_keep))goto malformed;break;\n } }\n malformed:r->report->malformed=1;return 0;\n}\n") + } else { + g.pf("%s", " default:"+g.unknownEvent()+"if(!table_message_skip(r,entry))goto malformed;break;\n } }\n malformed:r->report->malformed=1;return 0;\n}\n") + } + if !g.retain && !g.isVar(st.Name) && !st.IsMapEntry() { + g.emitFixedMessageRead(st) + } +} + +func (g *tableGen) emitFixedMessageRead(st *ir.Struct) { + g.pf("static SCHEMA_UNUSED int %s(%s * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report)\n{\n", g.api(st.Name, "load_messages"), st.Name) + g.pf(" TableReport ignored={0};TableMessageReader r;int64_t capacity,bodies,i;if(report==NULL)report=&ignored;\n if(values==NULL||count==NULL){report->malformed=1;return 0;}capacity=*count;*count=0;\n bodies=table_message_open(&r,vocabulary,buffer,bytes,report);if(bodies<0)return 0;\n if(bodies>capacity){*count=bodies;report->refused=1;report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE;return 0;}\n for(i=0;iid==UINT64_C(0x%016x)){\n", readType, g.sym(n, "message_key"), readType, ir.MapKeyWireId) + g.pf(" out.kind_bad=entry->kind!=%d && !table_kind_widens(entry->kind,%d);if(entry->kind!=%d && !out.kind_bad)out.widened=1;\n if(!out.kind_bad){\n", kind, kind, kind) + if key.Type.Kind == ir.TString { + g.pf(" uint64_t count;if(!table_bit_get(&r->bits,&count,table_bits_required(0,entry->max))||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)count*8))goto malformed;\n out.key.data=(const char *)(r->bits.buffer+(r->bits.offset>>3));out.key.length=(int32_t)count;out.over=count>%d;\n if(!table_wire_utf8((const uint8_t *)out.key.data,count))goto malformed;r->bits.offset+=(int64_t)count*8;\n", key.Type.Size) + } else { + g.pf(" uint64_t low,high;if(!table_message_number(r,entry,&low,&high))goto malformed;(void)high;out.over=0;\n") + bits := tableKindWidth(kind) * 8 + if bits < 64 { + lo, hi := messageRange(ir.TableKindSigned(kind), bits) + if ir.TableKindSigned(kind) { + g.pf(" if((int64_t)low<%s || (int64_t)low>%s)out.over=1;\n", tableIntLit(lo, true, 8), tableIntLit(hi, true, 8)) + } else { + g.pf(" if(low>%s)out.over=1;\n", tableIntLit(hi, false, 8)) + } + } + g.pf(" out.key=(%s)low;\n", g.mapKeyType(f)) + } + g.pf(" continue;} }\n if(!table_message_skip(r,entry))goto malformed;\n }malformed:out.malformed=1;return out;\n}\n") +} + +func (g *tableGen) emitMessageSequenceRead(f *ir.Field, dst, e, ind string) { + n := g.sequenceType(f) + elem := sequenceElement(f) + g.pf("%s{uint64_t count,i;TableSequenceFill fill;TableMessageEntry element_shape=table_message_element(%s);const TableMessageEntry * element_entry=&element_shape;(void)element_entry;\n", ind, e) + g.pf("%s if(!table_message_count(r,%s,&count))goto malformed;\n", ind, e) + g.retainReplace(f, ind) + g.pf("%s fill=table_sequence_fill(r->nodes?r->nodes->sink:NULL,&%s,count,sizeof(%s),SCHEMA_TABLE_ALIGNOF(%s));\n", ind, dst, n, n) + g.pf("%s if(fill.refused){if(r->nodes)r->nodes->refused=1;return 0;}if(!fill.ok)goto malformed;\n", ind) + if f.IsMap() { + g.pf("%s { %s previous; %s * last=NULL;int widened=0;memset(&previous,0,sizeof(previous));(void)element_entry;\n", ind, g.sym(n, "key_read"), n) + } + g.pf("%s for(i=0;ireport->widened++;}\n", ind) + g.pf("%s if(key.malformed)goto malformed;if(key.kind_bad){r->report->kind_mismatch++;r->bits=scan.bits;for(i++;ireport->clamped++;r->bits=scan.bits;continue;}order=last?%s(previous.key,key.key):-1;if(order>0)goto malformed;\n", ind, g.sym(n, "key_compare")) + g.pf("%s if(order==0){item=last;r->report->duplicate++;}else item=(%s *)table_sequence_fill_next(&fill);if(item==NULL)goto malformed;\n", ind, n) + g.retainIndex(f, "fill.value->count-1", ind+" ") + g.messageReadElement(elem, "(*item)", "element_entry", ind+" ") + g.pf("%s last=item;previous=key;\n", ind) + } else { + g.pf("%s item=(%s *)table_sequence_fill_next(&fill);if(item==NULL)goto malformed;\n", ind, n) + g.retainIndex(f, "i", ind+" ") + g.messageReadElement(elem, "(*item)", "element_entry", ind+" ") + } + g.pf("%s }\n", ind) + if f.IsMap() { + g.pf("%s }\n", ind) + } + g.pf("%s table_sequence_fill_end(&fill); }\n", ind) +} diff --git a/internal/codegen/ctable/message_save.go b/internal/codegen/ctable/message_save.go new file mode 100644 index 000000000..46ca139a1 --- /dev/null +++ b/internal/codegen/ctable/message_save.go @@ -0,0 +1,257 @@ +package ctable + +import ( + "fmt" + "strconv" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +func (g *tableGen) messageSlot(entry ir.TableVocabularyEntry) int { + if g.messageSlots == nil { + g.messageSlots = ir.TableVocabularySlots(g.unit) + } + if slot := g.messageSlots[entry.Key()]; slot > 0 { + return int(slot) + } + panic("message entry is absent from the unit vocabulary: " + entry.Key()) +} + +func (g *tableGen) messageHeader(e ir.TableVocabularyEntry, ind string) { + g.pf("%stable_bit_put(w,%d,kTableMessageRefBitsHere);\n", ind, g.messageSlot(e)) +} + +func (g *tableGen) messageEnum(e *ir.Enum, expr, ind string) { + g.pf("%sswitch(%s) {\n%s case %s: table_bit_put(w,0,kTableMessageRefBitsHere); break;\n", ind, expr, ind, enumNoneConst(e.Name)) + for i, v := range e.Variants { + entry := ir.TableVocabularyEntry{Id: ir.TableWireId(e.VariantWireName(i))} + g.pf("%s case %s: table_bit_put(w,%d,kTableMessageRefBitsHere); break;\n", ind, enumConst(e.Name, v), g.messageSlot(entry)) + } + g.pf("%s default: return 0;\n%s}\n", ind, ind) +} + +// Scalar arithmetic is unsigned at the stored width, including 128-bit bases. +func (g *tableGen) messageScalar(f *ir.Field, kind uint8, shape ir.TableMessageShape, expr, ind string) { + if e := enumRef(f); e != nil { + g.messageEnum(e, expr, ind) + return + } + bits := ir.TableMessageValueBits(kind, shape) + switch int(kind) { + case tkBool: + g.pf("%stable_bit_put(w,%s ? 1 : 0,1);\n", ind, expr) + case tkF32: + if shape.Packing == ir.TableMessageQuantized { + count, delta, _ := ir.TableMessageQuantization(shape) + g.pf("%stable_bit_put(w,table_message_quantize(%s,%s,%s,%du),%d);\n", ind, expr, formatFloat(float64(shape.QMin), true), formatFloat(float64(delta), true), count, bits) + } else { + g.pf("%stable_bit_put(w,table_float_to_bits(%s),32);\n", ind, expr) + } + case tkF64: + g.pf("%stable_bit_put(w,table_double_to_bits(%s),64);\n", ind, expr) + default: + var lo, hi uint64 + if shape.Base != nil { + low, high := wideLanes(shape.Base) + lo, hi = low.Uint64(), high.Uint64() + } + if f.Type.Width == 128 { + borrow := "0" + if lo != 0 { + borrow = fmt.Sprintf("((%s).lo 64 { + g.pf("%s table_bit_put(w,high,%d);\n", ind, bits-64) + } + g.pf("%s}\n", ind) + } else { + g.pf("%stable_bit_put(w,(uint64_t)(%s)-UINT64_C(%d),%d);\n", ind, expr, lo, bits) + } + } +} + +func (g *tableGen) messageValue(f *ir.Field, e ir.TableVocabularyEntry, expr, count, ind string) { + switch { + case f.IsList() || f.IsMap(): + elem, typ := sequenceElement(f), g.sequenceType(f) + g.pf("%s{ TableSequenceCursor cursor=%s; int32_t i;\n", ind, g.sequenceCursor(f, "w->nodes", expr)) + g.pf("%s if(!cursor.ok || %s.count<0) return 0; table_bit_put(w,(uint64_t)%s.count,32);\n", ind, expr, expr) + if ir.TableMessageAligns(e.Kind, e.Shape) { + g.pf("%stable_bit_align_write(w);\n", ind) + } + g.pf("%s for(i=0;i<%s.count;i++) { const %s * element=(const %s *)table_sequence_next(&cursor); if(element==NULL) return 0;\n", ind, expr, typ, typ) + g.messageElement(elem, e.Shape.Elem, e.Shape.Inner, "(*element)", ind+" ") + g.pf("%s } }\n", ind) + case f.Type.Pointer && f.Array == ir.ArrayNone: + g.pf("%s{ const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&%s); uint64_t index=table_number_find(w->nodes,node);\n", ind, expr) + g.pf("%s if(node!=NULL && index==0) return 0; table_bit_put(w,index,w->index_bits); }\n", ind) + case f.Type.Kind == ir.TString || f.Type.Kind == ir.TBytes || f.Type.Kind == ir.TWString: + g.pf("%sif(%s<0 || %s>%d) return 0;\n", ind, count, count, f.Type.Size) + bits := ir.TableMessageBitsRequired(0, e.Shape.Max) + if f.Type.Kind == ir.TBytes { + bits = ir.TableMessageCountBits(e.Shape) + } + g.pf("%stable_bit_put(w,(uint64_t)%s,%d);\n", ind, count, bits) + if f.Type.Kind == ir.TWString { + g.pf("%s{ int32_t i; for(i=0;i<%s;i++) table_bit_put(w,(uint16_t)%s[i],16); }\n", ind, count, expr) + } else { + g.pf("%stable_bit_align_write(w); table_bit_putbytes(w,%s,%s);\n", ind, expr, count) + } + case f.KeyEnum != "": + g.pf("%s{ uint32_t count=0; int32_t i;\n", ind) + g.pf("%s for(i=0;i<%d;i++) {\n", ind, f.ArrayBound) + g.messageKeyedRides(f, expr+"[i]", ind+" ") + g.pf("%s count++; }\n%s table_bit_put(w,count,%d);\n", ind, ind, ir.TableMessageCountBits(e.Shape)) + g.pf("%s for(i=0;i<%d;i++) {\n", ind, f.ArrayBound) + g.messageKeyedRides(f, expr+"[i]", ind+" ") + g.messageEnum(f.KeyEnumRef, "i+1", ind+" ") + g.messageElement(f, e.Shape.Elem, e.Shape.Inner, expr+"[i]", ind+" ") + g.pf("%s } }\n", ind) + case f.Array != ir.ArrayNone: + g.pf("%sif(%s<0 || %s>%d) return 0;\n", ind, count, count, f.ArrayBound) + g.pf("%stable_bit_put(w,(uint64_t)%s-%d,%d);\n", ind, count, e.Shape.Min, ir.TableMessageCountBits(e.Shape)) + if ir.TableMessageAligns(e.Kind, e.Shape) { + g.pf("%stable_bit_align_write(w);\n", ind) + } + g.pf("%s{ int32_t i; for(i=0;i<%s;i++) {\n", ind, count) + g.messageElement(f, e.Shape.Elem, e.Shape.Inner, expr+"[i]", ind+" ") + g.pf("%s} }\n", ind) + default: + g.messageElement(f, e.Kind, &e.Shape, expr, ind) + } +} + +func (g *tableGen) messageElement(f *ir.Field, kind uint8, shape *ir.TableMessageShape, expr, ind string) { + if f.Type.Pointer { + g.messageValue(&ir.Field{Type: f.Type}, ir.TableVocabularyEntry{}, expr, "", ind) + return + } + switch int(kind) { + case tkTable: + g.pf("%sif(!%s(w,&%s)) return 0;\n", ind, g.api(f.Type.Name, "save_message_body"), expr) + case tkUnion: + g.pf("%sif(!%s(w,&%s)) return 0;\n", ind, g.unionWireName(f.Type.Ref.(*ir.Union), "message_save"), expr) + default: + if shape == nil { + shape = &ir.TableMessageShape{} + } + g.messageScalar(f, kind, *shape, expr, ind) + } +} + +func (g *tableGen) messageKeyedRides(f *ir.Field, expr, ind string) { + if ir.TableWireScalarKind(f) == tkTable { + g.pf("%sTableBitWriter probe=*w; probe.buffer=NULL; probe.capacity=INT64_MAX; probe.bits=0; probe.check_default=1;\n", ind) + g.pf("%sif(!%s(&probe,&%s)) return 0;\n%sif(probe.bits==kTableMessageRefBitsHere) continue;\n", ind, g.api(f.Type.Name, "save_message_body"), expr, ind) + } else { + g.pf("%sif(%s) continue;\n", ind, g.wireDefaultEquals(f, expr)) + } +} + +func (g *tableGen) emitMessageSaveDeclarations(members []*ir.Struct, unions []*ir.Union) { + for _, st := range members { + g.pf("static SCHEMA_UNUSED int %s(TableBitWriter *,const %s *);\n", g.api(st.Name, "save_message_body"), st.Name) + } + for _, un := range unions { + g.pf("static SCHEMA_UNUSED int %s(TableBitWriter *,const %s *);\n", g.unionWireName(un, "message_save"), un.Name) + } +} + +func (g *tableGen) emitMessageUnionSave(un *ir.Union) { + g.pf("static SCHEMA_UNUSED int %s(TableBitWriter * w,const %s * value)\n{\n switch(value->type) {\n", g.unionWireName(un, "message_save"), un.Name) + g.pf(" case %s: table_bit_put(w,0,kTableMessageRefBitsHere); break;\n", enumNoneConst(un.Name+"Type")) + for _, v := range un.Variants { + g.pf(" case %s: {\n", enumConst(un.Name+"Type", v.Name)) + e := ir.TableArmEntry(v) + g.messageHeader(e, " ") + if !v.Void() { + count := strconv.FormatInt(v.F.ArrayBound, 10) + if armCompanioned(v) { + count = armValue("(*value)", v) + "_count" + if v.F.Array == ir.ArrayNone { + count = armValue("(*value)", v) + "_length" + } + } + g.messageValue(v.F, e, armValue("(*value)", v), count, " ") + } + g.pf(" break; }\n") + } + g.pf(" default: return 0;\n } return !w->overflow;\n}\n") +} + +func (g *tableGen) emitMessageSave(st *ir.Struct) { + g.pf("static SCHEMA_UNUSED int %s(TableBitWriter * w,const %s * value)\n{\n (void)value;\n", g.api(st.Name, "save_message_body"), st.Name) + guards := tableGuardExprs(st) + for _, f := range st.Fields { + expr, kind := "value->"+f.Name, ir.TableWireScalarKind(f) + g.pf(" { /* %s */\n", f.Name) + if guard := guards[f.Name]; guard != "" { + g.pf(" if(%s) {\n", guard) + } + condition := "!(" + g.wireDefaultEquals(f, expr) + ")" + count := strconv.FormatInt(f.ArrayBound, 10) + switch { + case f.IsList() || f.IsMap(): + condition = expr + ".count>0" + g.pf(" if(%s.count<0) return 0;\n", expr) + case !f.Type.Blob() && (f.Type.Kind == ir.TBytes || f.Type.Kind == ir.TString || f.Type.Kind == ir.TWString): + count = expr + "_length" + g.pf(" if(%s<0 || %s>%d) return 0;\n", count, count, f.Type.Size) + condition = g.wireBytesDiffer(f, expr) + case f.Array == ir.ArrayCounted: + count = expr + "_count" + g.pf(" if(%s<0 || %s>%d) return 0;\n", count, count, f.ArrayBound) + condition = count + ">0" + case f.Type.Optional: + condition = expr + "_present" + case f.KeyEnum != "": + g.pf(" int rides=0; { int32_t i; for(i=0;i<%d;i++) {\n", f.ArrayBound) + g.messageKeyedRides(f, expr+"[i]", " ") + g.pf(" rides=1; break; } }\n") + condition = "rides" + case f.Array == ir.ArrayFixed: + condition = "1" + if kind != tkTable { + g.pf(" int rides=0; { int32_t i; for(i=0;i<%d;i++) if(!(%s)) { rides=1; break; } }\n", f.ArrayBound, g.wireDefaultEquals(f, expr+"[i]")) + condition = "rides" + } + case kind == tkTable: + g.pf(" TableBitWriter probe=*w; probe.buffer=NULL; probe.capacity=INT64_MAX; probe.bits=0; probe.check_default=1;\n if(!%s(&probe,&%s)) return 0;\n", g.api(f.Type.Name, "save_message_body"), expr) + condition = "probe.bits>kTableMessageRefBitsHere" + case kind == tkUnion: + condition = expr + ".type!=" + enumNoneConst(f.Type.Name+"Type") + } + if f.Type.Optional { + condition = expr + "_present" + } + g.pf(" if(%s) {\n if(w->check_default) {w->bits=kTableMessageRefBitsHere+1;return 1;}\n", condition) + g.messageHeader(ir.TableFieldEntry(f), " ") + g.messageValue(f, ir.TableFieldEntry(f), expr, count, " ") + g.pf(" }\n") + if guards[f.Name] != "" { + g.pf(" }\n") + } + g.pf(" }\n") + } + g.pf(" table_bit_put(w,0,kTableMessageRefBitsHere); return !w->overflow;\n}\n") + if !g.isVar(st.Name) && !st.IsMapEntry() { + g.emitFixedMessageSave(st) + } +} + +func (g *tableGen) emitFixedMessageSave(st *ir.Struct) { + for _, measure := range []bool{true, false} { + verb, extra, buf, cap := "save_messages", ",uint8_t * buffer,int64_t capacity,TableReport * report", "buffer", "capacity" + if measure { + verb, extra, buf, cap = "measure_messages", ",TableReport * report", "NULL", "INT64_MAX" + } + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * values,int64_t count%s)\n{\n", g.api(st.Name, verb), st.Name, extra) + g.pf(" TableBitWriter w=table_bit_writer(%s,%s); int64_t i;\n if(count<1 || values==NULL) return -1;\n if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1;report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE;}return -1;}\n", buf, cap) + if !measure { + g.pf(" if(buffer==NULL || capacity<0) return -1;\n") + } + g.pf(" table_bit_put(&w,2,8); table_bit_put(&w,(uint64_t)(count-1),8);\n for(i=0;i 0 { + if vars := g.varMembers(members); len(vars) > 0 && !g.fileWire() { g.pf("/* ---- pointer-graph walkers: pack (Lock), size (Load) ---- */\n\n") for _, st := range vars { g.pf("static SCHEMA_UNUSED int64_t %s( const TableCtx * ctx, const %s * value, int32_t depth );\n", g.api(st.Name, "pack_measure"), st.Name) @@ -192,6 +207,16 @@ func (g *tableGen) emitVariableSurface(members []*ir.Struct) { if !g.anyVariable { return } + if g.fileWire() { + g.emitGraphBodies(members) + for _, st := range members { + if g.isVar(st.Name) { + g.emitBuilderAndPublicSurface(st) + g.emitMessageGraph(st) + } + } + return + } for _, st := range g.varMembers(members) { g.owner = st g.emitPackMeasure(st) @@ -427,6 +452,9 @@ func (g *tableGen) emitLoadMeasureBody(st *ir.Struct) { // ---- the builder and the public surface ---- func (g *tableGen) emitBuilderAndPublicSurface(st *ir.Struct) { + if st.IsMapEntry() { + return + } n := st.Name g.pf("/* ---- %s: the variable-length life (docs/SPEC-TABLES.md §2, §6, §9) ----\n", n) g.pf("\n") @@ -453,9 +481,9 @@ func (g *tableGen) emitBuilderAndPublicSurface(st *ir.Struct) { g.pf("/* Allocate a node in THIS thread's front: no lock, no atomic per node. One\n") g.pf(" worker per thread; allocate on your own, and synchronize your own writes\n") g.pf(" to nodes another worker allocated (§6.4). */\n") - g.pf("static SCHEMA_UNUSED int %s( %sBuilder * builder )\n{\n", g.api(n, "builder_init"), n) + g.pf("static SCHEMA_UNUSED int %s( %sBuilder * builder, TableAllocator allocator )\n{\n", g.api(n, "builder_init_with_allocator"), n) g.pf(" uint32_t at;\n") - g.pf(" table_arena_init( &builder->arena );\n") + g.pf(" table_arena_init_with_allocator( &builder->arena, allocator );\n") g.pf(" builder->main = table_worker_make( &builder->arena );\n") g.pf(" builder->root_ref.value = 0;\n") g.pf(" builder->region = NULL;\n") @@ -469,9 +497,11 @@ func (g *tableGen) emitBuilderAndPublicSurface(st *ir.Struct) { g.pf(" %s( (%s *) (void *) table_arena_at( &builder->arena, at ) );\n", g.api(n, "reset"), n) g.pf(" return 1;\n}\n\n") + g.pf("static SCHEMA_UNUSED int %s(%sBuilder * builder) { return %s(builder,table_default_allocator()); }\n\n", g.api(n, "builder_init"), n, g.api(n, "builder_init_with_allocator")) + g.pf("static SCHEMA_UNUSED void %s( %sBuilder * builder )\n{\n", g.api(n, "builder_shutdown"), n) g.pf(" table_arena_shutdown( &builder->arena );\n") - g.pf(" free( builder->region );\n") + g.pf(" table_release(builder->arena.allocator,builder->region);\n") g.pf(" builder->region = NULL;\n builder->region_bytes = 0;\n}\n\n") g.pf("/* The mutable root, or NULL once the builder is locked. */\n") @@ -479,6 +509,11 @@ func (g *tableGen) emitBuilderAndPublicSurface(st *ir.Struct) { g.pf(" if ( builder->arena.locked || builder->root_ref.value == 0 ) { return NULL; }\n") g.pf(" return (%s *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value );\n}\n\n", n) + if g.fileWire() { + g.emitGraphLock(st) + g.emitGraphPublic(st) + return + } g.pf("/* Lock is ONE WAY and it is the compaction: the segmented arena becomes one\n") g.pf(" exact-packed region with zero slack, references rewritten self-relative,\n") g.pf(" and the mutable life released. Single-threaded: call it after the workers\n") diff --git a/internal/codegen/ctable/retain.go b/internal/codegen/ctable/retain.go new file mode 100644 index 000000000..87c1cde44 --- /dev/null +++ b/internal/codegen/ctable/retain.go @@ -0,0 +1,221 @@ +package ctable + +import ( + "fmt" + "github.com/mas-bandwidth/schema/v2/ir" + "strings" +) + +// Retention owns two caller-sized stores. Its records are private resolved +// fields: directory node, declaration/index path, full IDs and patched lengths. +// The ordinary codec carries none of this state. +func (g *tableGen) retainRuntime() string { + var ids strings.Builder + for _, id := range ir.TableWireIds(g.unit) { + fmt.Fprintf(&ids, "UINT64_C(0x%016x),", id) + } + return strings.NewReplacer("@DEPTH@", fmt.Sprint(g.retainDepth()), "@KNOWN@", ids.String(), "@RETAIN_OUT@", cRetainOut, "@RETAIN_MESSAGE@", cRetainMessage+cRetainGraph).Replace(cRetainRuntime) +} +func (g *tableGen) retainDepth() int { + var field func(*ir.Field) int + var record func(*ir.Struct) int + memo := map[string]int{} + record = func(st *ir.Struct) int { + if n, ok := memo[st.Name]; ok { + return n + } + n := 0 + for _, f := range st.Fields { + n = max(n, field(f)) + } + memo[st.Name] = n + return n + } + field = func(f *ir.Field) int { + if f.Type.Pointer { + return 0 + } + if f.IsMap() { + return 1 + record(f.MapEntry) + } + switch ref := f.Type.Ref.(type) { + case *ir.Struct: + return 1 + record(ref) + case *ir.Union: + n := 0 + for _, v := range ref.Variants { + if !v.Void() { + n = max(n, 1+field(v.F)) + } + } + return n + 1 + } + return 0 + } + n := 1 + for name := range ir.TableClosure(g.unit) { + st := g.unit.Tables[name] + if st == nil { + st = g.unit.Structs[name] + } + if st != nil { + n = max(n, record(st)) + } + } + return n +} + +const cRetainRuntime = ` +#ifndef SCHEMA_TABLE_RETAIN_RUNTIME +#define SCHEMA_TABLE_RETAIN_RUNTIME +#define SCHEMA_TABLE_RETAIN_DEPTH_MAX @DEPTH@ +#define SCHEMA_TABLE_RETAIN_WALK_MAX 64 +#define SCHEMA_TABLE_RETAIN_HEADER 26 + +typedef struct TableRetainId { uint64_t id; int32_t slot; } TableRetainId; +typedef struct TableRetain { + uint8_t * bytes; int64_t capacity,used; + TableRetainId * ids; int32_t id_capacity,id_used,count; + const uint8_t * base; const TableNodeDirEntry * directory; int64_t directory_count; +} TableRetain; +typedef struct TableRetainStep {uint32_t ordinal,index;} TableRetainStep; +typedef struct TableRetainPath { + const void * at; uint32_t node; int32_t depth; + TableRetainStep steps[SCHEMA_TABLE_RETAIN_DEPTH_MAX]; +} TableRetainPath; +typedef struct TableRetainIds { + TableWriteIds known; + int32_t slots[sizeof(((TableWriteIds *)0)->ids)/sizeof(uint64_t)]; + TableRetain * retain; int32_t count; int lost; +} TableRetainIds; +typedef struct TableRetainWalk {TableRetain * retain;TableRetainIds * ids;TableRetainPath path;} TableRetainWalk; +static const uint64_t table_retain_known[]={@KNOWN@}; +static SCHEMA_UNUSED uint32_t table_retain_u32(const uint8_t * p) +{return (uint32_t)p[0]|(uint32_t)p[1]<<8|(uint32_t)p[2]<<16|(uint32_t)p[3]<<24;} +static SCHEMA_UNUSED uint64_t table_retain_u64(const uint8_t * p) +{return table_retain_u32(p)|(uint64_t)table_retain_u32(p+4)<<32;} +static SCHEMA_UNUSED void table_retain_put32(uint8_t * p,uint32_t v) +{int i;for(i=0;i<4;i++)p[i]=(uint8_t)(v>>(8*i));} +static SCHEMA_UNUSED void table_retain_put64(uint8_t * p,uint64_t v) +{table_retain_put32(p,(uint32_t)v);table_retain_put32(p+4,(uint32_t)(v>>32));} +static SCHEMA_UNUSED int64_t table_retain_leb_bytes(uint64_t v) +{int64_t n=1;while(v>=128){v>>=7;n++;}return n;} +static SCHEMA_UNUSED int table_retain_reserved(uint64_t id) +{return id>=UINT64_MAX-2;} +static SCHEMA_UNUSED TableRetainPath table_retain_root(const void * at,uint32_t node) +{TableRetainPath p;memset(&p,0,sizeof(p));p.at=at;p.node=node;return p;} +static SCHEMA_UNUSED TableRetainWalk table_retain_step(TableRetainWalk keep,uint32_t ordinal,uint32_t index) +{if(keep.path.depth0&&keep.path.depth<=SCHEMA_TABLE_RETAIN_DEPTH_MAX)keep.path.steps[keep.path.depth-1].index=index;return keep;} +static SCHEMA_UNUSED uint8_t * table_retain_payload(uint8_t * record) +{return record+SCHEMA_TABLE_RETAIN_HEADER+(int64_t)table_retain_u32(record+8)*8;} +static SCHEMA_UNUSED const void * table_retain_at(const TableRetain * retain,const uint8_t * record) +{uint32_t node=table_retain_u32(record+4);if(!retain->directory||!node||node>retain->directory_count)return NULL;return retain->base+retain->directory[node-1].offset;} +static SCHEMA_UNUSED int table_retain_under(const TableRetain * retain,const uint8_t * record,const TableRetainPath * path,int field,uint32_t ordinal) +{ + uint32_t depth=table_retain_u32(record+8);int32_t i;const uint8_t * steps=record+SCHEMA_TABLE_RETAIN_HEADER; + if(path->depth<0||path->depth>SCHEMA_TABLE_RETAIN_DEPTH_MAX||table_retain_at(retain,record)!=path->at)return 0; + if(depth<(uint32_t)path->depth||(field&&depth==(uint32_t)path->depth))return 0; + for(i=0;idepth;i++)if(table_retain_u32(steps+8*i)!=path->steps[i].ordinal||table_retain_u32(steps+8*i+4)!=path->steps[i].index)return 0; + return !field||table_retain_u32(steps+8*path->depth)==ordinal; +} +static SCHEMA_UNUSED int table_retain_here(const TableRetain * retain,const uint8_t * record,const TableRetainPath * path) +{return table_retain_u32(record+8)==(uint32_t)path->depth&&table_retain_under(retain,record,path,0,0);} +static SCHEMA_UNUSED void table_retain_discard(TableRetainWalk keep,int field,uint32_t ordinal) +{ + TableRetain * r=keep.retain;int64_t read=0,write=0;int32_t i,kept=0;if(!r||!r->bytes)return; + for(i=0;icount;i++){uint32_t n=table_retain_u32(r->bytes+read);if(!table_retain_under(r,r->bytes+read,&keep.path,field,ordinal)){if(read!=write)memmove(r->bytes+write,r->bytes+read,n);write+=n;kept++;}read+=n;}r->used=write;r->count=kept; +} +static SCHEMA_UNUSED void table_retain_reset(TableRetain * retain,const TableNodeMap * nodes,const uint8_t * region) +{if(retain){retain->used=0;retain->id_used=0;retain->count=0;retain->base=region;retain->directory=nodes?nodes->entries:NULL;retain->directory_count=nodes?(int64_t)nodes->count:0;}} +static SCHEMA_UNUSED int table_retain_nameable(uint64_t id) +{int64_t lo=0,hi=(int64_t)(sizeof(table_retain_known)/sizeof(table_retain_known[0]))-1;while(lo<=hi){int64_t mid=lo+(hi-lo)/2;if(table_retain_known[mid]==id)return 1;if(table_retain_known[mid]retain;TableWriteIds * v=&ids->known; + if(record&&!table_retain_nameable(id)){ + if(!r){ids->lost=1;return 0;}for(i=0;iid_used;i++)if(r->ids[i].id==id)return (uint64_t)r->ids[i].slot; + if(!r->ids||r->id_used>=r->id_capacity||ids->count==INT32_MAX){ids->lost=1;return 0;} + r->ids[r->id_used].id=id;r->ids[r->id_used++].slot=++ids->count;return (uint64_t)ids->count; + } + for(i=0;icount;i++)if(v->ids[i]==id)return (uint64_t)ids->slots[i]; + if(v->count==(int)(sizeof(v->ids)/sizeof(v->ids[0]))||ids->count==INT32_MAX){*overflow=1;return 0;} + v->ids[v->count]=id;ids->slots[v->count++]=++ids->count;return (uint64_t)ids->count; +} +static SCHEMA_UNUSED void table_retain_truncate(TableRetainIds * ids,int32_t mark) +{while(ids->known.count&&ids->slots[ids->known.count-1]>mark)ids->known.count--;if(ids->retain)while(ids->retain->id_used&&ids->retain->ids[ids->retain->id_used-1].slot>mark)ids->retain->id_used--;ids->count=mark;} +static SCHEMA_UNUSED void table_retain_writer_id(TableWriter * w,uint64_t id,TableRetainWalk keep) +{table_writer_leb(w,table_retain_ref(keep.ids,id,0,&w->overflow));} +static SCHEMA_UNUSED TableWriter table_retain_probe(const TableWriter * w,TableRetainWalk keep) +{TableWriter p=*w;p.buffer=NULL;p.capacity=INT64_MAX;p.offset=0;p.overflow=0;p.id_checkpoint=keep.ids->count;return p;} +static SCHEMA_UNUSED void table_retain_rewind(const TableWriter * probe,TableRetainWalk keep) +{table_retain_truncate(keep.ids,probe->id_checkpoint);} +static SCHEMA_UNUSED void table_retain_finish(TableWriter * w,TableRetainIds * ids) +{int32_t i=0,j=0;TableRetain * r=ids->retain;while(iknown.count||(r&&jid_used)){if(!r||j>=r->id_used||(iknown.count&&ids->slots[i]ids[j].slot))table_writer_put64(w,ids->known.ids[i++]);else table_writer_put64(w,r->ids[j++].id);}table_writer_put64(w,(uint64_t)ids->count);} + +/* Capture writes only within the remaining record capacity. Failure leaves + used/count unchanged; a huge zero-bit message count reaches this bound + before it can command an unbounded transcode. */ +typedef struct TableRetainIn { + TableReader input;uint8_t * out;int64_t capacity,used;int failed; +} TableRetainIn; +static SCHEMA_UNUSED int table_retain_raw(TableRetainIn * s,const void * data,int64_t bytes) +{if(s->failed||bytes<0||s->used>s->capacity||bytes>s->capacity-s->used){s->failed=1;return 0;}if(bytes)memcpy(s->out+s->used,data,(size_t)bytes);s->used+=bytes;return 1;} +static SCHEMA_UNUSED int table_retain_leb(TableRetainIn * s,uint64_t v) +{uint8_t b[10];int n=0;do{b[n++]=(uint8_t)((v&127)|(v>=128?128:0));v>>=7;}while(v);return table_retain_raw(s,b,n);} +static SCHEMA_UNUSED int table_retain_id(TableRetainIn * s,uint64_t id) +{uint8_t b[8];table_retain_put64(b,id);return table_retain_raw(s,b,8);} +static SCHEMA_UNUSED int table_retain_in_ref(TableRetainIn * s,int zero) +{uint64_t ref,id;if(!table_reader_leb(&s->input,&ref))return 0;if(!ref)return zero&&table_retain_id(s,0);if(ref>s->input.id_count)return 0;id=table_reader_id_at(&s->input,ref);return id&&!table_retain_reserved(id)&&table_retain_id(s,id);} +static SCHEMA_UNUSED int table_retain_in_payload(TableRetainIn *,uint8_t,int32_t); +static SCHEMA_UNUSED int table_retain_in_content(TableRetainIn *,uint8_t,int64_t,int32_t); +static SCHEMA_UNUSED int table_retain_in_frame(TableRetainIn * s,uint8_t kind,int64_t length,int32_t depth) +{uint8_t zero[8]={0};int64_t mark=s->used;if(!table_retain_raw(s,zero,8)||!table_retain_in_content(s,kind,length,depth))return 0;if(s->used-mark-8>UINT32_MAX)return 0;table_retain_put32(s->out+mark,(uint32_t)(s->used-mark-8));return 1;} +static SCHEMA_UNUSED int table_retain_in_content(TableRetainIn * s,uint8_t kind,int64_t length,int32_t depth) +{ + int64_t end;TableReader * r=&s->input;if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX||!table_reader_has(r,length))return 0;end=r->offset+length; + switch(kind){ + case 13:for(;;){uint64_t ref;int64_t mark=r->offset;uint8_t k;if(!table_reader_leb(r,&ref))return 0;if(!ref){if(!table_retain_id(s,0))return 0;break;}r->offset=mark;if(!table_retain_in_ref(s,0)||r->offset>=end)return 0;k=table_reader_get8(r);if(!table_retain_raw(s,&k,1)||!table_retain_in_payload(s,k,depth)||r->offset>end)return 0;}break; + case 14:case 16:{uint8_t elem;uint64_t n,i;if(r->offset>=end)return 0;elem=table_reader_get8(r);if(!table_retain_raw(s,&elem,1)||!table_reader_leb(r,&n)||!table_retain_leb(s,n))return 0; + for(i=0;i(uint64_t)(end-r->offset)||!table_retain_in_frame(s,elem,(int64_t)bytes,depth+1))return 0;}else if(!table_retain_in_payload(s,elem,depth))return 0;if(r->offset>end)return 0;}break;} + case 15:case 30:if(!table_retain_in_payload(s,kind,depth))return 0;break; + case 17:return 0; + default:if(!table_retain_raw(s,r->buffer+r->offset,length))return 0;r->offset+=length;break; + }return r->offset==end; +} +static SCHEMA_UNUSED int table_retain_width(uint8_t kind) +{switch(kind){case 1:case 2:case 6:case 20:case 25:return 1;case 3:case 7:case 21:case 26:return 2;case 4:case 8:case 10:case 22:case 27:return 4;case 5:case 9:case 11:case 23:case 28:return 8;case 18:case 19:case 24:case 29:return 16;default:return 0;}} +static SCHEMA_UNUSED int table_retain_in_payload(TableRetainIn * s,uint8_t kind,int32_t depth) +{ + TableReader * r=&s->input;uint64_t length;int width=table_retain_width(kind); + if(width){if(!table_reader_has(r,width)||!table_retain_raw(s,r->buffer+r->offset,width))return 0;r->offset+=width;return 1;} + switch(kind){ + case 12:case 31:case 32:case 33:if(!table_reader_leb(r,&length)||!table_reader_room(r,length)||!table_retain_leb(s,length)||!table_retain_raw(s,r->buffer+r->offset,(int64_t)length))return 0;r->offset+=(int64_t)length;return 1; + case 13:case 14:case 16:if(!table_reader_leb(r,&length)||!table_reader_room(r,length))return 0;return table_retain_in_frame(s,kind,(int64_t)length,depth+1); + case 15:{uint64_t arm;uint8_t k;int64_t mark=r->offset;if(!table_reader_leb(r,&arm))return 0;r->offset=mark;if(!table_retain_in_ref(s,1))return 0;if(!arm)return 1;if(!table_reader_has(r,1))return 0;k=table_reader_get8(r);if(!table_retain_raw(s,&k,1)||!table_reader_leb(r,&length)||!table_reader_room(r,length))return 0;return table_retain_in_frame(s,k,(int64_t)length,depth+1);} + case 30:return table_retain_in_ref(s,1); + default:return 0; + } +} +static SCHEMA_UNUSED uint8_t * table_retain_begin(TableRetainWalk keep,uint64_t id,uint8_t kind,TableRetainIn * s) +{ + TableRetain * r=keep.retain;int64_t header=SCHEMA_TABLE_RETAIN_HEADER+8*(int64_t)keep.path.depth;uint8_t * p;int32_t i; + if(table_retain_reserved(id)||!r||!r->bytes||keep.path.depth<0||keep.path.depth>SCHEMA_TABLE_RETAIN_DEPTH_MAX||r->used<0||r->used>r->capacity||header>r->capacity-r->used||r->count==INT32_MAX)return NULL; + p=r->bytes+r->used;table_retain_put32(p+4,keep.path.node);table_retain_put32(p+8,(uint32_t)keep.path.depth);table_retain_put64(p+16,id);p[24]=kind;p[25]=0; + for(i=0;iout=p+header;s->capacity=r->capacity-r->used-header;if(s->capacity>UINT32_MAX-header)s->capacity=UINT32_MAX-header;return p; +} +static SCHEMA_UNUSED void table_retain_commit(TableRetainWalk keep,uint8_t * record,const TableRetainIn * s,TableReport * report) +{int64_t bytes=SCHEMA_TABLE_RETAIN_HEADER+8*(int64_t)keep.path.depth+s->used;table_retain_put32(record,(uint32_t)bytes);table_retain_put32(record+12,(uint32_t)s->used);keep.retain->used+=bytes;keep.retain->count++;report->retained++;} +static SCHEMA_UNUSED int table_retain_capture(TableReader * r,uint64_t id,uint8_t kind,TableRetainWalk keep) +{ + TableRetainIn s;TableReader source=*r;uint8_t * record;if(!table_reader_skip(r,kind))return 0;if(!keep.retain)return 1; + record=table_retain_begin(keep,id,kind,&s);if(!record){r->report->retain_lost++;return 1;}source.size=r->offset;s.input=source; + if(!table_retain_in_payload(&s,kind,0)||s.input.offset!=source.size){r->report->retain_lost++;return 1;}table_retain_commit(keep,record,&s,r->report);return 1; +} + +@RETAIN_OUT@ +@RETAIN_MESSAGE@ +#endif +` diff --git a/internal/codegen/ctable/retain_emit.go b/internal/codegen/ctable/retain_emit.go new file mode 100644 index 000000000..c88e73f24 --- /dev/null +++ b/internal/codegen/ctable/retain_emit.go @@ -0,0 +1,246 @@ +package ctable + +import ( + "github.com/mas-bandwidth/schema/v2/ir" + "strings" +) + +// The parallel family uses the same field emitters. Only named body/helper +// calls gain the retention context; primitive reads and writes stay unchanged. +// This pass operates on compiler-generated C identifiers, skipping comments +// and literals, and balances arguments before adding the context parameter. +func retainedCode(source string, names map[string]string, required ...string) string { + hits := make(map[string]bool) + result := retainedCodeWalk(source, names, hits) + for _, name := range required { + if !hits[name] { + panic("missing generated retention anchor: " + name) + } + } + return result +} + +func retainedCodeWalk(source string, names map[string]string, hits map[string]bool) string { + var out strings.Builder + for i := 0; i < len(source); { + if end := retainedLiteralEnd(source, i); end > i { + out.WriteString(source[i:end]) + i = end + continue + } + if !retainedIdent(source[i]) || source[i] >= '0' && source[i] <= '9' { + out.WriteByte(source[i]) + i++ + continue + } + j := i + 1 + for j < len(source) && retainedIdent(source[j]) { + j++ + } + word := source[i:j] + replacement, ok := names[word] + open := j + for open < len(source) && strings.ContainsRune(" \t\r\n", rune(source[open])) { + open++ + } + if !ok || open == len(source) || source[open] != '(' { + out.WriteString(word) + i = j + continue + } + close, depth := open+1, 1 + for close < len(source) && depth > 0 { + if end := retainedLiteralEnd(source, close); end > close { + close = end + continue + } + if source[close] == '(' { + depth++ + } + if source[close] == ')' { + depth-- + } + close++ + } + if depth != 0 { + panic("unbalanced generated retention call: " + word) + } + args := source[open+1 : close-1] + hits[word] = true + decl := strings.HasPrefix(strings.TrimSpace(args), "TableWriter") || strings.HasPrefix(strings.TrimSpace(args), "TableReader") || strings.HasPrefix(strings.TrimSpace(args), "TableMessageReader") + out.WriteString(replacement) + out.WriteString(source[j : open+1]) + out.WriteString(retainedCodeWalk(args, names, hits)) + if decl { + out.WriteString(", TableRetainWalk retention") + } else { + out.WriteString(", retention") + } + out.WriteByte(')') + i = close + if decl { + brace := i + for brace < len(source) && strings.ContainsRune(" \t\r\n", rune(source[brace])) { + brace++ + } + if brace < len(source) && source[brace] == '{' { + out.WriteString(source[i : brace+1]) + out.WriteString("\n (void)retention;\n") + i = brace + 1 + } + } + } + return out.String() +} +func retainedIdent(b byte) bool { + return b == '_' || b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z' || b >= '0' && b <= '9' +} +func retainedLiteralEnd(s string, i int) int { + if i+1 < len(s) && s[i:i+2] == "//" { + if n := strings.IndexByte(s[i:], '\n'); n >= 0 { + return i + n + } + return len(s) + } + if i+1 < len(s) && s[i:i+2] == "/*" { + if n := strings.Index(s[i+2:], "*/"); n >= 0 { + return i + 2 + n + 2 + } + return len(s) + } + if s[i] != '\'' && s[i] != '"' { + return i + } + quote := s[i] + for j := i + 1; j < len(s); j++ { + if s[j] == '\\' { + j++ + continue + } + if s[j] == quote { + return j + 1 + } + } + return len(s) +} +func (g *tableGen) emitRetainBodies(members []*ir.Struct, unions []*ir.Union) { + if !g.anyVariable { + return + } + names := map[string]string{"table_writer_id": "table_retain_writer_id", "table_writer_probe": "table_retain_probe", "table_writer_rewind": "table_retain_rewind"} + var required []string + for _, st := range members { + for _, verb := range []string{"save_body", "load_body", "load_message_body"} { + required = append(required, g.api(st.Name, verb)) + } + } + for name := range ir.TableClosure(g.unit) { + for _, verb := range []string{"save_body", "load_body", "load_message_body"} { + fn := g.api(name, verb) + names[fn] = fn + "_retain" + } + } + vocabulary := ir.TableClosureVocabulary(g.unit) + for _, file := range g.unit.Files { + for _, d := range file.Decls { + if un, ok := d.(*ir.Union); ok && vocabulary[un.Name] { + g.retainUnionNames(names, un) + } + } + for _, un := range file.TableUnions { + g.retainUnionNames(names, un) + } + } + for _, st := range members { + for _, f := range st.Fields { + fn := g.wireFieldHelper(st, f) + names[fn] = fn + "_retain" + } + } + previous := g.body.String() + g.body.Reset() + g.retain = true + for _, st := range members { + g.pf("static SCHEMA_UNUSED int %s(TableWriter *,const %s *);\n", g.api(st.Name, "save_body"), st.Name) + g.pf("static SCHEMA_UNUSED int %s(TableReader *,%s *);\n", g.api(st.Name, "load_body"), st.Name) + } + g.emitUnionWireDeclarations(unions) + g.emitMessageReadDeclarations(members, unions) + for _, un := range unions { + g.emitUnionWire(un) + g.emitMessageUnionRead(un) + } + for _, st := range members { + g.owner = st + g.emitWireWrite(st) + g.emitWireRead(st) + g.emitMessageRead(st) + } + g.retain = false + g.owner = nil + code := g.body.String() + g.body.Reset() + g.body.WriteString(previous) + g.body.WriteString(retainedCode(code, names, required...)) +} +func (g *tableGen) retainUnionNames(names map[string]string, un *ir.Union) { + for _, verb := range []string{"save", "load", "message_load"} { + fn := g.unionWireName(un, verb) + names[fn] = fn + "_retain" + } + for _, v := range un.Variants { + fn := g.sym(un.Name, "wire_arm_"+v.Name) + names[fn] = fn + "_retain" + } +} +func retainHoldsBodies(f *ir.Field) bool { + if f.IsMap() { + return true + } + if f.Type.Pointer { + return false + } + switch f.Type.Ref.(type) { + case *ir.Struct, *ir.Union: + return true + } + return false +} +func (g *tableGen) retainReadField(f *ir.Field, ordinal int, ind string) { + if !g.retain { + return + } + g.pf("%sretention=table_retain_step(body_keep,%d,0);\n", ind, ordinal) + if retainHoldsBodies(f) && !f.IsMap() && !f.IsList() && f.Array == ir.ArrayNone { + g.pf("%stable_retain_discard(body_keep,1,%d);\n", ind, ordinal) + } +} +func (g *tableGen) retainReplace(f *ir.Field, ind string) { + if !g.retain || !retainHoldsBodies(f) { + return + } + // retention names this field. Its parent and ordinal identify the entire old + // occurrence, including array elements absent from the new occurrence. + g.pf("%s{TableRetainWalk parent_keep=retention;uint32_t ordinal=parent_keep.path.steps[--parent_keep.path.depth].ordinal;table_retain_discard(parent_keep,1,ordinal);}\n", ind) +} +func (g *tableGen) retainIndex(f *ir.Field, index, ind string) { + if g.retain && retainHoldsBodies(f) { + g.pf("%sretention=table_retain_index(retention,(uint32_t)(%s));\n", ind, index) + } +} + +func (g *tableGen) retainUnionDiscard() string { + if g.retain { + return "table_retain_discard(retention,0,0);" + } + return "" +} + +// Unknown enum values, arms and keyed slots are excluded retention classes. +// Emit their event directly so whitespace changes cannot lose retain_lost. +func (g *tableGen) unknownEvent() string { + if g.retain { + return "r->report->unknown++;r->report->retain_lost += retention.retain != NULL;" + } + return "r->report->unknown++;" +} diff --git a/internal/codegen/ctable/retain_emit_test.go b/internal/codegen/ctable/retain_emit_test.go new file mode 100644 index 000000000..2dbc281c7 --- /dev/null +++ b/internal/codegen/ctable/retain_emit_test.go @@ -0,0 +1,28 @@ +package ctable + +import ( + "strings" + "testing" +) + +func TestRetainedCodeAnchors(t *testing.T) { + names := map[string]string{"load_body": "load_body_retain", "table_writer_id": "table_retain_writer_id"} + source := `/* load_body(r,v) */ +static int load_body (TableReader * r, Value * v) { table_writer_id (w, nested(1,2)); return 1; } +const char * text="load_body(r,v)"; +` + got := retainedCode(source, names, "load_body", "table_writer_id") + for _, want := range []string{`/* load_body(r,v) */`, `"load_body(r,v)"`, `load_body_retain (TableReader * r, Value * v, TableRetainWalk retention)`, `table_retain_writer_id (w, nested(1,2), retention)`} { + if !strings.Contains(got, want) { + t.Fatalf("missing %q in %s", want, got) + } + } + t.Run("missing", func(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("a missing required anchor was silent") + } + }() + retainedCode(strings.ReplaceAll(source, "load_body (", "load_body_renamed ("), names, "load_body") + }) +} diff --git a/internal/codegen/ctable/retain_graph.go b/internal/codegen/ctable/retain_graph.go new file mode 100644 index 000000000..07479c36c --- /dev/null +++ b/internal/codegen/ctable/retain_graph.go @@ -0,0 +1,124 @@ +package ctable + +import ( + "github.com/mas-bandwidth/schema/v2/ir" +) + +const cRetainGraph = ` +typedef int (*TableRetainSaveNode)(TableWriter *,const void *,uint64_t,TableRetainWalk); +static SCHEMA_UNUSED int table_retain_records(TableWriter * w,TableRetainWalk retention,TableRetainSaveNode save) +{ + uint64_t i;const TableNumbering * n=w->nodes;table_writer_leb(w,n->count-1); + for(i=1;icount;i++){const TableNodeEntry * e=n->entries+i;retention.path=table_retain_root(e->value,0);table_retain_writer_id(w,e->type->id,retention); + if(!w->buffer){int64_t begin=w->offset;if(!save(w,e->value,e->type->id,retention))return 0;table_writer_leb(w,(uint64_t)(w->offset-begin));} + else {TableWriter probe=table_retain_probe(w,retention);if(!save(&probe,e->value,e->type->id,retention))return 0;table_retain_rewind(&probe,retention);table_writer_leb(w,(uint64_t)probe.offset);if(!save(w,e->value,e->type->id,retention))return 0;}} + return !w->overflow; +} +static SCHEMA_UNUSED int64_t table_retain_graph_save(const void * root,const TableNodeType * type,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator,TableRetainSaveNode save) +{ + TableNumbering numbering;TableRetainIds ids;TableRetainWalk retention;TableWriter w;int64_t result=-1; + memset(&ids,0,sizeof(ids));ids.retain=retain;memset(&retention,0,sizeof(retention));retention.retain=retain;retention.ids=&ids;retention.path=table_retain_root(root,1); + if(retain)retain->id_used=0;if(buffer)table_retain_clear_placed(retain);w=table_writer_make(buffer,capacity,&ids.known); + if(!table_number_build(&numbering,NULL,root,type,allocator)){table_number_dispose(&numbering);return -1;}w.nodes=&numbering; + table_writer_put8(&w,1);if(!save(&w,root,type->id,retention))goto done;w.offset--; + if(numbering.count>1){table_retain_writer_id(&w,UINT64_MAX,retention);table_writer_put8(&w,12); + if(!buffer){int64_t begin=w.offset;if(!table_retain_records(&w,retention,save))goto done;table_writer_leb(&w,(uint64_t)(w.offset-begin));} + else {TableWriter probe=table_retain_probe(&w,retention);if(!table_retain_records(&probe,retention,save))goto done;table_retain_rewind(&probe,retention);table_writer_leb(&w,(uint64_t)probe.offset);if(!table_retain_records(&w,retention,save))goto done;}} + table_writer_put8(&w,0);table_retain_finish(&w,&ids);if(!w.overflow){result=w.offset;if(buffer)table_retain_count_lost(retain,report);} + done:table_number_dispose(&numbering);return result; +} +` + +func (g *tableGen) emitRetainRoots(members []*ir.Struct) { + if !g.anyVariable { + return + } + for _, st := range members { + if !g.isVar(st.Name) || st.IsMapEntry() { + continue + } + g.emitRetainFileRoot(st) + g.emitRetainMessageRoot(st) + } +} +func (g *tableGen) emitRetainFileRoot(st *ir.Struct) { + n := st.Name + // Reuse the graph fill itself with retained body calls; region sizing and + // allocation are unchanged. No plain load executes a retention branch. + previous := g.body.String() + g.body.Reset() + g.retain = true + g.emitGraphLoad(st) + g.retain = false + names := map[string]string{g.sym(n, "load_graph"): g.sym(n, "load_graph_retain")} + for name := range ir.TableClosure(g.unit) { + fn := g.api(name, "load_body") + names[fn] = fn + "_retain" + } + code := g.body.String() + g.body.Reset() + g.body.WriteString(previous) + g.body.WriteString(retainedCode(code, names, g.sym(n, "load_graph"))) + g.pf("static SCHEMA_UNUSED const %s * %s(uint8_t * region,int64_t region_bytes,const uint8_t * wire,int64_t wire_bytes,TableRetain * retain,TableReport * report)\n{\n", n, g.api(n, "load_retain")) + g.pf(" TableReport ignored={0};TableReader reader;TableNodeMap nodes;TableNodeDirEntry * directory;TableRegionSink place;TableSink sink;TableRetainWalk retention;int64_t data,records,total;\n if(report==NULL)report=&ignored;table_retain_reset(retain,NULL,NULL);\n if(!table_wire_open(&reader,wire,wire_bytes,report))return NULL;total=%s(&reader,&data,&records);\n if(total<0||region==NULL||region_bytesmalformed=1;return NULL;}\n memset(region,0,(size_t)total);directory=(TableNodeDirEntry *)(void *)(region+data);directory[0].offset=0;directory[0].type_id=UINT64_C(0x%016x);\n memset(&nodes,0,sizeof(nodes));nodes.base=region;nodes.entries=directory;nodes.count=(uint64_t)records+1;place.base=region;place.capacity=data;place.used=%s(&reader);sink.region=&place;sink.worker=NULL;\n table_retain_reset(retain,&nodes,region);memset(&retention,0,sizeof(retention));retention.retain=retain;retention.path=table_retain_root(region,1);\n if(!%s(&reader,(%s *)(void *)region,&nodes,directory,&sink,retention))return NULL;return (const %s *)(const void *)region;\n}\n", g.sym(n, "load_layout"), ir.TableWireId(st.WireName()), g.sym(n, "wire_storage"), g.sym(n, "load_graph_retain"), n, n) + dispatch := g.sym(n, "retain_save_node") + g.pf("static SCHEMA_UNUSED int %s(TableWriter * w,const void * value,uint64_t id,TableRetainWalk retention)\n{\n switch(id){\n", dispatch) + seen := map[string]bool{} + for _, t := range append([]*ir.Struct{st}, ir.PointerReachable(st)...) { + if seen[t.Name] { + continue + } + seen[t.Name] = true + g.pf(" case UINT64_C(0x%016x):return %s_retain(w,(const %s *)value,retention);\n", ir.TableWireId(t.WireName()), g.api(t.Name, "save_body"), t.Name) + } + b, s := ir.PointerReachableBlobs(st) + for _, blob := range []struct { + yes bool + id uint64 + }{{b, ir.BytesWireTypeId}, {s, ir.StringWireTypeId}} { + if blob.yes { + g.pf(" case UINT64_C(0x%016x):{const TableBlob * blob=(const TableBlob *)value;table_writer_raw(w,blob+1,blob->length);return !w->overflow;}\n", blob.id) + } + } + g.pf(" default:return 0;\n }\n}\n") + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * root,TableRetain * retain,TableAllocator allocator)\n{if(!root)return -1;return table_retain_graph_save(root,&%s,retain,NULL,INT64_MAX,NULL,allocator,%s);}\n", g.api(n, "measure_retain_with_allocator"), n, g.graphType(n), dispatch) + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * root,TableRetain * retain)\n{return %s(root,retain,table_default_allocator());}\n", g.api(n, "measure_retain"), n, g.api(n, "measure_retain_with_allocator")) + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator)\n{if(!root||!buffer||capacity<0||!report)return -1;return table_retain_graph_save(root,&%s,retain,buffer,capacity,report,allocator,%s);}\n", g.api(n, "save_retain_with_allocator"), n, g.graphType(n), dispatch) + g.pf("static SCHEMA_UNUSED int64_t %s(const %s * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report)\n{return %s(root,retain,buffer,capacity,report,table_default_allocator());}\n", g.api(n, "save_retain"), n, g.api(n, "save_retain_with_allocator")) +} + +func (g *tableGen) emitRetainMessageRoot(st *ir.Struct) { + n := st.Name + previous := g.body.String() + g.body.Reset() + g.retain = true + g.emitMessageGraphLoad(st) + g.retain = false + names := map[string]string{g.sym(n, "message_load_into"): g.sym(n, "message_load_into_retain")} + for name := range ir.TableClosure(g.unit) { + fn := g.api(name, "load_message_body") + names[fn] = fn + "_retain" + } + code := g.body.String() + g.body.Reset() + g.body.WriteString(previous) + g.body.WriteString(retainedCode(code, names, g.sym(n, "message_load_into"))) + g.pf("static SCHEMA_UNUSED int %s(const %s ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableRetain * retains,TableReport * report)\n{\n", g.api(n, "load_retain_messages"), n) + g.pf(" TableReport ignored={0};TableMessageReader r;TableRetainWalk retention;int64_t bodies,capacity,used=0,i;if(!report)report=&ignored;\n if(!roots||!count){report->malformed=1;return 0;}capacity=*count;*count=0;bodies=table_message_open(&r,vocabulary,buffer,bytes,report);if(bodies<0)return 0;\n if(bodies>capacity){*count=bodies;report->refused=1;report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE;return 0;}\n if(!region||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1;return 0;}memset(region,0,(size_t)region_bytes);\n for(i=0;ioutput.used;if(!table_retain_raw(&s->output,zero,8)||!table_retain_message_content(s,e,depth))return 0;if(s->output.used-mark-8>UINT32_MAX)return 0;table_retain_put32(s->output.out+mark,(uint32_t)(s->output.used-mark-8));return 1;} +static SCHEMA_UNUSED int table_retain_message_scalar(TableRetainMessage * s,const TableMessageEntry * e) +{ + uint64_t lo=0,hi=0;uint8_t bytes[16];int width=table_retain_width(e->kind);if(!width)return 0; + if(e->kind==10&&e->packing==2){float value;if(!table_message_float(&s->input,e,&value))return 0;lo=table_float_to_bits(value);} + else if(!table_message_number(&s->input,e,&lo,&hi))return 0; + table_retain_put64(bytes,lo);table_retain_put64(bytes+8,hi);return table_retain_raw(&s->output,bytes,width); +} +static SCHEMA_UNUSED int table_retain_message_opaque(TableRetainMessage * s,const TableMessageEntry * e,int framed) +{ + uint64_t n,i;TableMessageReader * r=&s->input; + if(e->kind==12||e->kind==33){if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0;if(e->kind==33)n*=2;else if(!table_bit_align_read(&r->bits))return 0;} + else if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + if(n>INT64_MAX/8||!table_bit_has(&r->bits,(int64_t)n*8))return 0; + if(framed&&!table_retain_leb(&s->output,n))return 0; + if(n>(uint64_t)(s->output.capacity-s->output.used))return 0; + for(i=0;ibits,&v,8))return 0;byte=(uint8_t)v;if(!table_retain_raw(&s->output,&byte,1))return 0;}return 1; +} +static SCHEMA_UNUSED int table_retain_message_content(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{ + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX)return 0; + switch(e->kind){ + case 13:for(;;){const TableMessageEntry * f;if(!table_message_ref(&s->input,&f,0))return 0;if(!f)return table_retain_id(&s->output,0);if(!f->id||!table_retain_id(&s->output,f->id)||!table_retain_raw(&s->output,&f->kind,1)||!table_retain_message_payload(s,f,depth))return 0;} + case 14:case 16:{uint64_t n,i;TableMessageEntry elem=table_message_element(e);if(elem.kind==17||!table_message_count(&s->input,e,&n)||!table_retain_raw(&s->output,&elem.kind,1)||!table_retain_leb(&s->output,n))return 0; + for(i=0;ikind==16){const TableMessageEntry * key;if(!table_message_ref(&s->input,&key,1)||!key||!key->id||!table_retain_id(&s->output,key->id)||!table_retain_message_frame(s,&elem,depth+1))return 0;}else if(!table_retain_message_payload(s,&elem,depth))return 0;}return 1;} + case 15:case 30:return table_retain_message_payload(s,e,depth); + case 32:return 1; + case 12:case 31:case 33:return table_retain_message_opaque(s,e,0); + case 0:case 17:return 0; + default:return table_retain_message_scalar(s,e); + } +} +static SCHEMA_UNUSED int table_retain_message_payload(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{ + switch(e->kind){ + case 0:case 17:return 0; + case 32:return table_retain_leb(&s->output,0); + case 30:{const TableMessageEntry * entry;if(!table_message_ref(&s->input,&entry,1))return 0;return table_retain_id(&s->output,entry?entry->id:0);} + case 15:{const TableMessageEntry * arm;if(!table_message_ref(&s->input,&arm,0))return 0;if(!arm)return table_retain_id(&s->output,0);return arm->id&&arm->kind&&table_retain_id(&s->output,arm->id)&&table_retain_raw(&s->output,&arm->kind,1)&&table_retain_message_frame(s,arm,depth+1);} + case 13:case 14:case 16:return table_retain_message_frame(s,e,depth+1); + case 12:case 31:case 33:return table_retain_message_opaque(s,e,1); + default:return table_retain_message_scalar(s,e); + } +} +static SCHEMA_UNUSED int table_retain_message_capture(TableMessageReader * r,const TableMessageEntry * entry,TableRetainWalk keep) +{ + TableRetainMessage s;uint8_t * record;s.input=*r;if(!table_message_skip(r,entry))return 0;if(!keep.retain)return 1; + record=table_retain_begin(keep,entry->id,entry->kind,&s.output); + if(!record||!table_retain_message_payload(&s,entry,0)||s.input.bits.offset!=r->bits.offset){r->report->retain_lost++;return 1;} + table_retain_commit(keep,record,&s.output,r->report);return 1; +} +` diff --git a/internal/codegen/ctable/retain_save.go b/internal/codegen/ctable/retain_save.go new file mode 100644 index 000000000..1cf5e10e5 --- /dev/null +++ b/internal/codegen/ctable/retain_save.go @@ -0,0 +1,71 @@ +package ctable + +// A postorder measure caches each resolved frame's encoded length. Emission +// then reads that length once; hostile nesting cannot cause repeated measuring. +const cRetainOut = ` +typedef struct TableRetainOut { + uint8_t * input;int64_t size,at,bytes;TableRetainIds * ids;TableWriter * writer;int overflow; +} TableRetainOut; +static SCHEMA_UNUSED int table_retain_out_raw(TableRetainOut * s,const void * data,int64_t bytes) +{if(bytes<0||bytes>INT64_MAX-s->bytes)return 0;if(s->writer)table_writer_raw(s->writer,data,bytes);s->bytes+=bytes;return !s->writer||!s->writer->overflow;} +static SCHEMA_UNUSED int table_retain_out_leb(TableRetainOut * s,uint64_t v) +{if(s->writer)table_writer_leb(s->writer,v);s->bytes+=table_retain_leb_bytes(v);return !s->writer||!s->writer->overflow;} +static SCHEMA_UNUSED int table_retain_out_leb_read(TableRetainOut * s,uint64_t * v) +{TableReader r=table_reader_make(s->input,s->size,NULL);r.offset=s->at;if(!table_reader_leb(&r,v))return 0;s->at=r.offset;return 1;} +static SCHEMA_UNUSED int table_retain_out_ref(TableRetainOut * s) +{uint64_t id,ref;if(s->size-s->at<8)return 0;id=table_retain_u64(s->input+s->at);s->at+=8;if(!id)return table_retain_out_leb(s,0);ref=table_retain_ref(s->ids,id,1,&s->overflow);return !s->ids->lost&&!s->overflow&&table_retain_out_leb(s,ref);} +static SCHEMA_UNUSED int table_retain_out_payload(TableRetainOut *,uint8_t,int32_t); +static SCHEMA_UNUSED int table_retain_out_content(TableRetainOut *,uint8_t,int64_t,int32_t); +static SCHEMA_UNUSED int table_retain_out_frame(TableRetainOut * s,uint8_t kind,int32_t depth) +{ + uint8_t * slot;int64_t resolved,wire,begin;if(s->size-s->at<8)return 0;slot=s->input+s->at;resolved=table_retain_u32(slot);s->at+=8; + if(!s->writer){begin=s->bytes;if(!table_retain_out_content(s,kind,resolved,depth))return 0;wire=s->bytes-begin;if(wire>UINT32_MAX)return 0;table_retain_put32(slot+4,(uint32_t)wire);s->bytes+=table_retain_leb_bytes((uint64_t)wire);return 1;} + wire=table_retain_u32(slot+4);if(!table_retain_out_leb(s,(uint64_t)wire))return 0;begin=s->bytes;return table_retain_out_content(s,kind,resolved,depth)&&s->bytes-begin==wire; +} +static SCHEMA_UNUSED int table_retain_out_content(TableRetainOut * s,uint8_t kind,int64_t length,int32_t depth) +{ + int64_t end;if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX||length<0||length>s->size-s->at)return 0;end=s->at+length; + switch(kind){ + case 13:for(;;){uint64_t id;uint8_t k;if(end-s->at<8)return 0;id=table_retain_u64(s->input+s->at);if(!table_retain_out_ref(s))return 0;if(!id)break;if(s->at>=end)return 0;k=s->input[s->at++];if(!table_retain_out_raw(s,&k,1)||!table_retain_out_payload(s,k,depth))return 0;}break; + case 14:case 16:{uint64_t n,i;uint8_t elem;if(s->at>=end)return 0;elem=s->input[s->at++];if(!table_retain_out_raw(s,&elem,1)||!table_retain_out_leb_read(s,&n)||!table_retain_out_leb(s,n))return 0; + for(i=0;iinput+s->at,length))return 0;s->at+=length;break; + }return s->at==end; +} +static SCHEMA_UNUSED int table_retain_out_payload(TableRetainOut * s,uint8_t kind,int32_t depth) +{ + int width=table_retain_width(kind);uint64_t length; + if(width){if(width>s->size-s->at||!table_retain_out_raw(s,s->input+s->at,width))return 0;s->at+=width;return 1;} + switch(kind){ + case 12:case 31:case 32:case 33:if(!table_retain_out_leb_read(s,&length)||length>(uint64_t)(s->size-s->at)||!table_retain_out_leb(s,length)||!table_retain_out_raw(s,s->input+s->at,(int64_t)length))return 0;s->at+=(int64_t)length;return 1; + case 13:case 14:case 16:return table_retain_out_frame(s,kind,depth+1); + case 15:{uint64_t arm;uint8_t k;if(s->size-s->at<8)return 0;arm=table_retain_u64(s->input+s->at);if(!table_retain_out_ref(s))return 0;if(!arm)return 1;if(s->at>=s->size)return 0;k=s->input[s->at++];return table_retain_out_raw(s,&k,1)&&table_retain_out_frame(s,k,depth+1);} + case 30:return table_retain_out_ref(s); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t table_retain_record_measure(uint8_t * record,TableRetainIds * ids,uint64_t * ref) +{ + int32_t mark=ids->count;TableRetainOut s;int overflow=0;ids->lost=0;*ref=table_retain_ref(ids,table_retain_u64(record+16),1,&overflow); + if(!ids->lost&&!overflow){memset(&s,0,sizeof(s));s.input=table_retain_payload(record);s.size=table_retain_u32(record+12);s.ids=ids;if(table_retain_out_payload(&s,record[24],0)&&s.at==s.size)return table_retain_leb_bytes(*ref)+1+s.bytes;} + table_retain_truncate(ids,mark);ids->lost=0;return -1; +} +static SCHEMA_UNUSED int table_retain_tail(TableWriter * w,TableRetainWalk keep) +{ + TableRetain * r=keep.retain;int32_t i;int64_t at=0;if(!r||!r->bytes)return 1; + for(i=0;icount;i++){uint8_t * record=r->bytes+at;uint64_t ref;int64_t bytes;TableRetainOut s;at+=table_retain_u32(record); + if(!table_retain_here(r,record,&keep.path))continue; + if(w->check_default){w->offset=2;return 1;} + bytes=table_retain_record_measure(record,keep.ids,&ref);if(bytes<0)continue; + if(!w->buffer){if(bytes>INT64_MAX-w->offset){w->overflow=1;return 0;}w->offset+=bytes;continue;} + table_writer_leb(w,ref);table_writer_put8(w,record[24]);memset(&s,0,sizeof(s));s.input=table_retain_payload(record);s.size=table_retain_u32(record+12);s.ids=keep.ids;s.writer=w; + if(!table_retain_out_payload(&s,record[24],0)||s.at!=s.size)return 0;record[25]=1; + }return !w->overflow; +} +static SCHEMA_UNUSED void table_retain_clear_placed(TableRetain * r) +{int32_t i;int64_t at=0;if(!r||!r->bytes)return;for(i=0;icount;i++){r->bytes[at+25]=0;at+=table_retain_u32(r->bytes+at);}} +static SCHEMA_UNUSED void table_retain_count_lost(TableRetain * r,TableReport * report) +{int32_t i;int64_t at=0;if(!r||!r->bytes)return;for(i=0;icount;i++){if(!r->bytes[at+25])report->retain_lost++;at+=table_retain_u32(r->bytes+at);}} +` diff --git a/internal/codegen/ctable/sequence.go b/internal/codegen/ctable/sequence.go new file mode 100644 index 000000000..fd7c572b6 --- /dev/null +++ b/internal/codegen/ctable/sequence.go @@ -0,0 +1,283 @@ +package ctable + +import ( + "github.com/mas-bandwidth/schema/v2/ir" +) + +// Lists and maps share a relocatable reference/count record. Arena segments +// retain element addresses through append and erase; region elements are one +// contiguous array inside the owning node's extent. +const tableSequenceRuntime = ` +#ifndef SCHEMA_TABLE_SEQUENCE_RUNTIME +#define SCHEMA_TABLE_SEQUENCE_RUNTIME +typedef struct TableSequence { TableRef items; int32_t count, padding; } TableSequence; +typedef struct TableSequenceHead { uint32_t first, last; int32_t live, reserved; } TableSequenceHead; +typedef struct TableSequenceSegment { uint32_t next, dead; int32_t used, reserved; } TableSequenceSegment; +typedef struct TableSequenceCursor +{ + const TableArena * arena; + const uint8_t * array; + const void * const * ordered; + const TableSequenceSegment * segment; + int64_t width; + int32_t index, left; + int ok; +} TableSequenceCursor; +static SCHEMA_UNUSED TableSequenceCursor table_sequence_cursor(const TableCtx * ctx, const TableSequence * value, int64_t width) +{ + TableSequenceCursor c; memset(&c,0,sizeof(c)); c.width=width; c.left=value->count; + c.ok=value->count>=0 && (value->count==0 || value->items.value!=0); + if(!c.ok || value->count==0) { return c; } + if(ctx!=NULL && ctx->arena!=NULL) { + const TableSequenceHead * head=(const TableSequenceHead *)table_arena_at(ctx->arena,(uint32_t)value->items.value); + c.arena=ctx->arena; c.ok=head!=NULL && head->live==value->count; + if(c.ok) { c.segment=(const TableSequenceSegment *)table_arena_at(c.arena,head->first); } + } else { c.array=(const uint8_t *)(const void *)&value->items+value->items.value; } + return c; +} +static SCHEMA_UNUSED const void * table_sequence_next(TableSequenceCursor * c) +{ + if(!c->ok || c->left<=0) { return NULL; } + if(c->ordered!=NULL) { c->left--; return c->ordered[c->index++]; } + if(c->array!=NULL) { const void * p=c->array+(int64_t)c->index++*c->width; c->left--; return p; } + while(c->segment!=NULL) { + while(c->indexsegment->used) { + int32_t i=c->index++; + if((c->segment->dead & (UINT32_C(1)<left--; return (const uint8_t *)(c->segment+1)+(int64_t)i*c->width; } + } + c->segment=c->segment->next ? (const TableSequenceSegment *)table_arena_at(c->arena,c->segment->next) : NULL; c->index=0; + } + c->ok=0; return NULL; +} +typedef struct TableSequenceOrder +{ + const TableSequence * value; + const void ** entries; + struct TableSequenceOrder * next; +} TableSequenceOrder; +typedef int (*TableSequenceCompare)(const void *,const void *); +static SCHEMA_UNUSED void table_sequence_sift(const void ** entries,int64_t at,int64_t count,TableSequenceCompare compare) +{ + for(;;) { + int64_t child=at*2+1; const void * swap; + if(child>=count) { return; } + if(child+1=0) { return; } + swap=entries[at]; entries[at]=entries[child]; entries[child]=swap; at=child; + } +} +// The arena's sort is cached only for the duration of one save or lock. +// Builders retain insertion order and stable addresses; no index is stored. +static SCHEMA_UNUSED TableSequenceCursor table_sequence_order(const TableCtx * ctx,const TableSequence * value,int64_t width, + TableSequenceCompare compare,TableAllocator allocator,TableSequenceOrder ** orders) +{ + TableSequenceCursor cursor=table_sequence_cursor(ctx,value,width); + TableSequenceOrder * order; int64_t i; + if(!cursor.ok || value->count==0 || ctx==NULL || ctx->arena==NULL) { return cursor; } + for(order=*orders;order!=NULL;order=order->next) { + if(order->value==value) { cursor.ordered=order->entries; return cursor; } + } + order=(TableSequenceOrder *)table_allocate(allocator,sizeof(*order)); + if(order==NULL) { cursor.ok=0; return cursor; } + order->entries=(const void **)table_allocate(allocator,(int64_t)value->count*sizeof(void *)); + if(order->entries==NULL) { table_release(allocator,order); cursor.ok=0; return cursor; } + for(i=0;icount;i++) { + order->entries[i]=table_sequence_next(&cursor); + if(order->entries[i]==NULL) { table_release(allocator,order->entries); table_release(allocator,order); return cursor; } + } + for(i=(int64_t)value->count/2;i>0;i--) { table_sequence_sift(order->entries,i-1,value->count,compare); } + for(i=(int64_t)value->count-1;i>0;i--) { + const void * swap=order->entries[0]; order->entries[0]=order->entries[i]; order->entries[i]=swap; + table_sequence_sift(order->entries,0,i,compare); + } + order->value=value; order->next=*orders; *orders=order; + cursor=table_sequence_cursor(ctx,value,width); cursor.ordered=order->entries; return cursor; +} +static SCHEMA_UNUSED void table_sequence_orders_release(TableAllocator allocator,TableSequenceOrder * order) +{ + while(order!=NULL) { TableSequenceOrder * next=order->next; table_release(allocator,order->entries); table_release(allocator,order); order=next; } +} +static SCHEMA_UNUSED void * table_sequence_add(TableWorker * worker,TableSequence * list,int64_t width) +{ + TableSequenceHead * head; TableSequenceSegment * segment; uint32_t at; + if(worker==NULL || worker->arena==NULL || worker->arena->locked || list->count<0 || list->count==INT32_MAX || width<=0 || width>(INT64_MAX-16)/32) { return NULL; } + if(list->items.value==0) { + at=table_worker_alloc(worker,sizeof(TableSequenceHead)); if(at==kTableAllocFailed) { return NULL; } + list->items.value=at; + } + head=(TableSequenceHead *)table_arena_at(worker->arena,(uint32_t)list->items.value); + if(head==NULL || head->live!=list->count) { return NULL; } + segment=head->last ? (TableSequenceSegment *)table_arena_at(worker->arena,head->last) : NULL; + if(segment==NULL || segment->used==32) { + at=table_worker_alloc(worker,16+32*width); if(at==kTableAllocFailed) { return NULL; } + if(segment!=NULL) { segment->next=at; } else { head->first=at; } + head->last=at; segment=(TableSequenceSegment *)table_arena_at(worker->arena,at); + } + { void * p=(uint8_t *)(segment+1)+(int64_t)segment->used++*width; head->live++; list->count++; return p; } +} +static SCHEMA_UNUSED int table_sequence_erase(TableArena * arena,TableSequence * list,const void * element,int64_t width) +{ + TableSequenceHead * head; uint32_t at; + if(arena==NULL || arena->locked || list->items.value==0 || element==NULL) { return 0; } + head=(TableSequenceHead *)table_arena_at(arena,(uint32_t)list->items.value); + for(at=head->first;at;) { + TableSequenceSegment * s=(TableSequenceSegment *)table_arena_at(arena,at); int32_t i; + for(i=0;iused;i++) { + if(element==(const uint8_t *)(s+1)+(int64_t)i*width && !(s->dead & (UINT32_C(1)<dead|=UINT32_C(1)<count--; head->live--; return 1; + } + } + at=s->next; + } + return 0; +} +static SCHEMA_UNUSED int table_extent_reserve(int64_t * at,int64_t count,int64_t width,int64_t alignment) +{ + if(count<0 || width<=0 || *at<0 || *at>INT64_MAX-alignment || count>(INT64_MAX-*at-alignment)/width) { return 0; } + *at=(*at+alignment-1)&~(alignment-1); *at+=count*width; return 1; +} +typedef struct TableSequenceFill +{ + TableSequence * value; TableWorker * worker; uint8_t * array; + int64_t width; int32_t capacity; int ok,refused; +} TableSequenceFill; +static SCHEMA_UNUSED TableSequenceFill table_sequence_fill(TableSink * sink,TableSequence * value,uint64_t count,int64_t width,int64_t alignment) +{ + TableSequenceFill f; memset(&f,0,sizeof(f)); f.value=value; f.width=width; + memset(value,0,sizeof(*value)); + if(sink==NULL) { return f; } + if(count>INT32_MAX) { f.refused=sink->worker!=NULL; return f; } + f.worker=sink->worker; f.capacity=(int32_t)count; + if(f.worker!=NULL) { f.ok=1; return f; } + if(sink->region!=NULL) { + TableRegionSink * region=sink->region; int64_t at=region->used; + if(!table_extent_reserve(&at,(int64_t)count,width,alignment) || at>region->capacity) { return f; } + f.array=region->base+at-(int64_t)count*width; region->used=at; + if(count) { value->items.value=(int64_t)(f.array-(uint8_t *)(void *)&value->items); } + f.ok=1; + } + return f; +} +static SCHEMA_UNUSED void * table_sequence_fill_next(TableSequenceFill * f) +{ + if(!f->ok || f->value->count>=f->capacity) { return NULL; } + if(f->worker!=NULL) { return table_sequence_add(f->worker,f->value,f->width); } + return f->array+(int64_t)f->value->count++*f->width; +} +static SCHEMA_UNUSED void table_sequence_fill_drop(TableSequenceFill * f,void * element) +{ + if(f->worker!=NULL) { table_sequence_erase(f->worker->arena,f->value,element,f->width); } + else if(f->value->count>0) { f->value->count--; } +} +static SCHEMA_UNUSED void table_sequence_fill_end(TableSequenceFill * f) +{ if(f->worker==NULL && f->value->count==0) { f->value->items.value=0; } } +#endif +` + +func sequenceElement(f *ir.Field) *ir.Field { + if f.IsMap() { + return &ir.Field{Type: ir.FieldType{Kind: ir.TNamed, Name: f.MapEntry.Name, Ref: f.MapEntry}} + } + e := *f + e.Array = ir.ArrayNone + e.Type.Optional = false + return &e +} + +func (g *tableGen) sequenceType(f *ir.Field) string { + e := sequenceElement(f) + if e.Type.Pointer { + g.noteRef(e.Type.Name) + return "TableRef" + } + return g.cFieldType(e.Type) +} + +func (g *tableGen) emitListSurface(st *ir.Struct, f *ir.Field) { + typ := g.sequenceType(f) + name := st.Name + ir.GoExportName(f.Name) + g.pf("static SCHEMA_UNUSED %s * %s(TableWorker * worker, TableList * list)\n{\n", typ, g.api(name, "add")) + g.pf(" %s * element=(%s *)table_sequence_add(worker,list,sizeof(%s)); if(element==NULL) { return NULL; }\n", typ, typ, typ) + if ref, ok := f.Type.Ref.(*ir.Struct); ok && !f.Type.Pointer { + g.pf(" %s(element);\n", g.api(ref.Name, "reset")) + } else { + g.pf(" memset(element,0,sizeof(*element));\n") + } + g.pf(" return element;\n}\n") + g.pf("static SCHEMA_UNUSED int %s(TableArena * arena,TableList * list,const %s * element)\n{ return table_sequence_erase(arena,list,element,sizeof(%s)); }\n", g.api(name, "erase"), typ, typ) + g.pf("static SCHEMA_UNUSED TableSequenceCursor %s(const TableCtx * ctx,const TableList * list)\n{ return table_sequence_cursor(ctx,list,sizeof(%s)); }\n", g.api(name, "each"), typ) + result := "const " + typ + " *" + if f.Type.Pointer { + result = "const " + f.Type.Name + " *" + } + g.pf("static SCHEMA_UNUSED %s %s(const TableList * list,int32_t index)\n{\n", result, g.api(name, "at")) + g.pf(" const %s * element; if(index<0 || index>=list->count || list->items.value==0) { return NULL; }\n", typ) + g.pf(" element=(const %s *)((const uint8_t *)(const void *)&list->items+list->items.value)+(int64_t)index;\n", typ) + if f.Type.Pointer { + g.pf(" return (const %s *)table_ref_at(NULL,element);\n", f.Type.Name) + } else { + g.pf(" return element;\n") + } + g.pf("}\n") +} + +func (g *tableGen) emitSequenceWrite(f *ir.Field, expr string) { + e := sequenceElement(f) + typ := g.sequenceType(f) + g.pf(" TableSequenceCursor cursor=%s; int32_t i;\n", g.sequenceCursor(f, "w->nodes", expr)) + g.pf(" if(!cursor.ok) { return 0; } table_writer_put8(w,%d); table_writer_leb(w,(uint64_t)%s.count);\n", ir.TableWireScalarKind(e), expr) + g.pf(" for(i=0;i<%s.count;i++) {\n const %s * element=(const %s *)table_sequence_next(&cursor); if(element==NULL) { return 0; }\n", expr, typ, typ) + g.retainIndex(f, "i", " ") + g.wirePayload(e, "(*element)", " ") + g.pf(" }\n") +} + +func (g *tableGen) emitListRead(f *ir.Field, dst, bounded string) { + e := sequenceElement(f) + typ := g.sequenceType(f) + kind := ir.TableWireScalarKind(e) + g.pf(" TableReader sub;\n") + if bounded == "" { + g.pf(" if(!table_reader_span(r,&sub)) { r->report->malformed=1; return 0; }\n") + } else { + g.pf(" sub=%s; %s.offset=%s.size;\n", bounded, bounded, bounded) + } + g.pf(" if(sub.size>=2) {\n uint8_t elem_kind; uint64_t count,i; TableSequenceFill fill;\n") + if bounded == "" { + g.pf(" if(!table_reader_array_header(&sub,r,&elem_kind,&count)) { r->report->malformed=1; break; }\n") + } else { + g.pf(" elem_kind=table_reader_get8(&sub); if(!table_reader_leb(&sub,&count)) { r->report->malformed=1; break; }\n") + } + g.pf(" if(elem_kind!=%d) { if(!(%s)) { r->report->kind_mismatch++; break; } r->report->widened++; }\n", kind, wireElementWidenTest(f, kind, "elem_kind")) + g.retainReplace(f, " ") + g.pf(" fill=table_sequence_fill(r->nodes ? r->nodes->sink : NULL,&%s,count,sizeof(%s),SCHEMA_TABLE_ALIGNOF(%s));\n if(fill.refused) { r->nodes->refused=1; return 0; }\n if(!fill.ok) { r->report->malformed=1; break; }\n", dst, typ, typ) + g.pf(" for(i=0;ireport->malformed=1; break; }\n", typ, typ) + g.retainIndex(f, "i", " ") + if ref, ok := e.Type.Ref.(*ir.Struct); ok && !e.Type.Pointer { + g.pf(" %s(element);\n", g.api(ref.Name, "reset")) + } + g.pf(" do {\n") + bad := "r->report->malformed=1; table_sequence_fill_drop(&fill,element); goto list_end_" + f.Name + ";" + switch kind { + case tkTable: + g.pf(" TableReader item; if(!table_reader_span(&sub,&item)) { %s }\n %s(&item,element);\n", bad, g.api(e.Type.Name, "load_body")) + g.wireRefusalCheck(" ") + g.pf(" if(item.offset!=item.size) { r->report->malformed=1; %s(element); }\n", g.api(e.Type.Name, "reset")) + case tkUnion: + g.pf(" if(!%s(&sub,element,1)) { %s }\n", g.unionWireName(e.Type.Ref.(*ir.Union), "load"), bad) + default: + g.wireScalarRead(e, "(*element)", "sub", "elem_kind", " ", bad) + } + g.pf(" } while(0);\n }\n list_end_%s: table_sequence_fill_end(&fill);\n }\n", f.Name) +} + +func sequenceFloor(f *ir.Field) int { + k := ir.TableWireScalarKind(sequenceElement(f)) + switch k { + case tkTable: + return 2 + case tkUnion, ir.TableKindEnum, 17: + return 1 + } + return tableKindWidth(k) +} diff --git a/internal/codegen/ctable/text.go b/internal/codegen/ctable/text.go new file mode 100644 index 000000000..c08bf2248 --- /dev/null +++ b/internal/codegen/ctable/text.go @@ -0,0 +1,35 @@ +package ctable + +const tableTextRuntime = ` +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif +` diff --git a/internal/codegen/ctable/unions.go b/internal/codegen/ctable/unions.go new file mode 100644 index 000000000..c5cc55fb4 --- /dev/null +++ b/internal/codegen/ctable/unions.go @@ -0,0 +1,304 @@ +package ctable + +import ( + "fmt" + "strings" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +func armCompanioned(v ir.UnionVariant) bool { + if v.Void() || v.F.Type.Pointer && v.F.Array == ir.ArrayNone { + return false + } + return v.F.Array == ir.ArrayCounted || v.F.Type.Kind == ir.TString || v.F.Type.Kind == ir.TWString || v.F.Type.Kind == ir.TBytes +} +func armValue(base string, v ir.UnionVariant) string { + base += ".as." + v.Name + if armCompanioned(v) { + base += ".value" + } + return base +} +func armWireKind(v ir.UnionVariant) int { + if v.Void() { + return ir.TableKindNoPayload + } + return ir.TableWireFieldKind(v.F) +} +func (g *tableGen) unionWireName(un *ir.Union, verb string) string { + return g.sym(un.Name, "wire_"+verb) +} + +// Storage dependencies are visited through union arms as well as table fields. +// Pointer edges do not participate: a reference's storage is already complete. +func (g *tableGen) emitTableShapes(members []*ir.Struct) { + done := map[string]bool{} + var field func(*ir.Field) + var decl func(string) + field = func(f *ir.Field) { + if f.IsMap() { + decl(f.MapEntry.Name) + return + } + if !f.Type.Pointer && f.Type.Kind == ir.TNamed { + decl(f.Type.Name) + } + } + decl = func(name string) { + if done[name] || g.declBase(name) != g.file.Base { + return + } + done[name] = true + if st := g.unit.Tables[name]; st != nil { + for _, f := range st.Fields { + field(f) + } + g.emitTableStruct(st) + } else if un := g.unit.TableUnions[name]; un != nil { + for _, v := range un.Variants { + if !v.Void() { + field(v.F) + } + } + g.emitTableUnion(un) + } + } + for _, st := range members { + if st.IsTable { + decl(st.Name) + } + } + for _, un := range g.file.TableUnions { + decl(un.Name) + } +} +func (g *tableGen) emitTableUnion(un *ir.Union) { + tag := un.Name + "Type" + g.pf("typedef uint%d_t %s;\n#define %s 0\n", ir.StorageBitsFor(int64(len(un.Variants))), tag, enumNoneConst(tag)) + for i, v := range un.Variants { + g.pf("#define %s %d\n", enumConst(tag, v.Name), i+1) + } + g.pf("#define %s %d\n#define %s %d\n", enumConst(tag, "Count"), len(un.Variants), enumConst(tag, "Max"), len(un.Variants)) + g.pf("typedef struct %s {\n %s type;\n", un.Name, tag) + payloads := 0 + for _, v := range un.Variants { + if !v.Void() { + payloads++ + } + } + if payloads > 0 { + g.pf(" union {\n") + for _, v := range un.Variants { + if v.Void() { + continue + } + f := *v.F + if armCompanioned(v) { + g.pf(" struct {\n") + f.Name = "value" + g.emitTableStorageField(&f) + g.pf(" } %s;\n", v.Name) + } else { + f.Name = v.Name + g.emitTableStorageField(&f) + } + } + g.pf(" } as;\n") + } + g.pf("} %s;\n\n", un.Name) +} +func (g *tableGen) fileWireUnions() []*ir.Union { + var out []*ir.Union + names := ir.TableClosureVocabulary(g.unit) + for _, d := range g.file.Decls { + if un, ok := d.(*ir.Union); ok && names[un.Name] { + out = append(out, un) + } + } + out = append(out, g.file.TableUnions...) + return out +} +func (g *tableGen) emitUnionWireDeclarations(unions []*ir.Union) { + for _, un := range unions { + g.pf("static SCHEMA_UNUSED int %s( TableWriter * w, const %s * value );\n", g.unionWireName(un, "save"), un.Name) + g.pf("static SCHEMA_UNUSED int %s( TableReader * r, %s * value, int element );\n", g.unionWireName(un, "load"), un.Name) + } +} + +func (g *tableGen) emitUnionWire(un *ir.Union) { + // One payload writer per arm: lengths are measured from the current + // vocabulary, exactly as a field's framed payload is measured. + for ordinal, v := range un.Variants { + if v.Void() { + continue + } + g.pf("static SCHEMA_UNUSED int %s( TableWriter * w, const %s * value )\n{\n", g.sym(un.Name, "wire_arm_"+v.Name), un.Name) + if g.retain { + g.pf(" retention=table_retain_step(retention,%d,0);\n", ordinal) + } + expr := armValue("(*value)", v) + g.wireBarePayload(v.F, expr, " ") + g.pf(" return !w->overflow;\n}\n\n") + } + g.pf("static SCHEMA_UNUSED int %s( TableWriter * w, const %s * value )\n{\n switch ( value->type )\n {\n", g.unionWireName(un, "save"), un.Name) + g.pf(" case %s: table_writer_leb( w, 0 ); break;\n", enumNoneConst(un.Name+"Type")) + for _, v := range un.Variants { + g.pf(" case %s:\n", enumConst(un.Name+"Type", v.Name)) + g.pf(" table_writer_id( w, 0x%016xull ); table_writer_put8( w, %d );\n", ir.TableWireId(v.WireName()), armWireKind(v)) + if v.Void() { + g.pf(" table_writer_leb( w, 0 );\n") + } else { + g.wireFrame(fmt.Sprintf("%s( w, value )", g.sym(un.Name, "wire_arm_"+v.Name)), " ") + } + g.pf(" break;\n") + } + g.pf(" default: return 0;\n }\n return !w->overflow;\n}\n\n") + g.pf("static SCHEMA_UNUSED int %s( TableReader * r, %s * value, int element )\n{\n", g.unionWireName(un, "load"), un.Name) + g.pf(" uint64_t ref; uint8_t kind; TableReader arm;\n") + if g.retain { + g.pf(" table_retain_discard(retention,0,0);\n") + } + g.pf(" if ( !table_reader_leb( r, &ref ) || ref > r->id_count ) { r->report->malformed = 1; return 0; }\n if ( ref == 0 ) { value->type = 0; return 1; }\n if ( element ) { value->type = 0; }\n if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; }\n kind = table_reader_get8( r );\n if ( !table_reader_span( r, &arm ) ) { r->report->malformed = 1; return 0; }\n value->type = 0;\n switch ( table_reader_id_at( r, ref ) )\n {\n") + for ordinal, v := range un.Variants { + g.pf(" case 0x%016xull:\n {\n", ir.TableWireId(v.WireName())) + k := armWireKind(v) + g.pf(" if ( kind != %d && !table_kind_widens( kind, %d ) ) { r->report->kind_mismatch++; break; }\n", k, k) + if !v.Void() && ir.ArmWireFixedWidth(v.F) > 0 { + g.pf(" switch ( kind )\n {\n") + for source := 1; source <= 29; source++ { + if source == k || ir.TableKindWidens(source, k) { + g.pf(" case %d: if ( arm.size != %d ) { r->report->malformed = 1; goto next_arm_%s; } break;\n", source, tableKindWidth(source), v.Name) + } + } + g.pf(" default: break;\n }\n") + } + if !v.Void() { + if g.retain { + g.pf(" retention=table_retain_step(retention,%d,0);\n", ordinal) + } + g.wireReadArm(v, armValue("(*value)", v), " ") + g.wireRefusalCheck(" ") + } + if k != tkUnion { + g.pf(" if ( arm.offset != arm.size ) { r->report->malformed = 1; break; }\n") + } + g.pf(" if ( kind != %d ) { r->report->widened++; }\n", k) + g.pf(" value->type = %s;\n", enumConst(un.Name+"Type", v.Name)) + if !v.Void() && ir.ArmWireFixedWidth(v.F) > 0 { + g.pf(" next_arm_%s: ;\n", v.Name) + } + g.pf(" break;\n }\n") + } + g.pf("%s", " default: "+g.unknownEvent()+" break;\n }\n return 1;\n}\n\n") +} + +// An arm's L is its field payload's frame: no second length surrounds it. +func (g *tableGen) wireBarePayload(f *ir.Field, expr, ind string) { + switch { + case f.IsList() || f.IsMap(): + g.emitSequenceWrite(f, expr) + case f.Type.Kind == ir.TString && !f.Type.Blob(): + g.pf("%sif ( %s_length < 0 || %s_length > %d ) { return 0; }\n", ind, expr, expr, f.Type.Size) + g.pf("%stable_writer_raw( w, %s, %s_length );\n", ind, expr, expr) + case f.Type.Kind == ir.TWString: + g.pf("%sif ( %s_length < 0 || %s_length > %d ) { return 0; }\n", ind, expr, expr, f.Type.Size) + g.pf("%s{ int32_t i; for ( i=0; i<%s_length; i++ ) { table_writer_put16( w, %s[i] ); } }\n", ind, expr, expr) + case f.Type.Kind == ir.TBytes && !f.Type.Blob(): + g.pf("%sif ( %s_length < 0 || %s_length > %d ) { return 0; }\n", ind, expr, expr, f.Type.Size) + g.pf("%stable_writer_put8( w, 6 ); table_writer_leb( w, (uint64_t)%s_length ); table_writer_raw( w, %s, %s_length );\n", ind, expr, expr, expr) + case f.Array != ir.ArrayNone: + n := fmt.Sprint(f.ArrayBound) + if f.Array == ir.ArrayCounted { + n = expr + "_count" + g.pf("%sif ( %s < 0 || %s > %d ) { return 0; }\n", ind, n, n, f.ArrayBound) + } + g.pf("%s{ int32_t i; table_writer_put8( w, %d ); table_writer_leb( w, (uint64_t)(%s) );\n", ind, ir.TableWireScalarKind(f), n) + g.pf("%s for ( i=0; i<%s; i++ ) {\n", ind, n) + g.retainIndex(f, "i", ind+" ") + g.wirePayload(f, expr+"[i]", ind+" ") + g.pf("%s }\n%s}\n", ind, ind) + case ir.TableWireScalarKind(f) == tkTable: + g.pf("%sif ( !%s( w, &%s ) ) { return 0; }\n", ind, g.api(f.Type.Name, "save_body"), expr) + default: + g.wirePayload(f, expr, ind) + } +} +func (g *tableGen) wireReadArm(v ir.UnionVariant, dst, ind string) { + f := v.F + k := armWireKind(v) + bad := "r->report->malformed = 1; break;" + switch { + case f.Type.Pointer && f.Array == ir.ArrayNone: + // Validate the whole framed index before resolving it. Trailing bytes + // damage this arm without producing a pointee-kind event. + target := ir.TableWireId(ir.PointeeWireName(f)) + if f.Type.Blob() { + target = ir.BlobWireTypeId(f) + } + g.pf("%suint64_t index; if (!table_reader_leb(&arm,&index) || arm.offset!=arm.size) { %s }\n", ind, bad) + g.pf("%stable_node_resolve(arm.nodes,&%s,index,UINT64_C(0x%016x),r->report);\n", ind, dst, target) + case v.Body(): + g.pf("%s%s( &arm, &%s );\n", ind, g.api(v.Type, "load_body"), dst) + case f.Type.Kind == ir.TString && !f.Type.Blob(): + g.pf("%suint64_t keep;\n%sif ( !table_wire_utf8( arm.buffer, (uint64_t)arm.size ) ) { %s }\n", ind, ind, bad) + g.pf("%skeep = table_wire_utf8_clamp( arm.buffer, (uint64_t)arm.size, %d ); if ( keep != (uint64_t)arm.size ) { r->report->clamped++; }\n", ind, f.Type.Size) + g.pf("%smemcpy( %s, arm.buffer, (size_t)keep ); %s[keep]=0; %s_length=(int32_t)keep; arm.offset=arm.size;\n", ind, dst, dst, dst) + case f.Type.Kind == ir.TWString: + g.pf("%sint64_t keep, i;\n%sif ( arm.size%%2 || !table_wire_utf16( arm.buffer, arm.size/2 ) ) { %s }\n", ind, ind, bad) + g.pf("%skeep=table_wire_utf16_clamp( arm.buffer, arm.size/2, %d ); if ( keep != arm.size/2 ) { r->report->clamped++; }\n", ind, f.Type.Size) + g.pf("%sfor ( i=0; ireport->malformed = 1; goto bad_arm_"+v.Name+";") + g.pf("%sgoto read_arm_%s;\n%sbad_arm_%s: break;\n%sread_arm_%s: ;\n", ind, v.Name, ind, v.Name, ind, v.Name) + } +} + +func (g *tableGen) emitUnionArmDescriptors(un *ir.Union) { + if g.descriptorUnions == nil { + g.descriptorUnions = map[string]bool{} + } + key := fmt.Sprintf("%s:%t", un.Name, g.outside) + if g.descriptorUnions[key] { + return + } + g.descriptorUnions[key] = true + st := &ir.Struct{Name: un.Name} + for _, v := range un.Variants { + if v.Void() || v.Body() { + continue + } + f := *v.F + f.Name = v.Name + g.emitFieldVocabulary(st, &f) + g.emitTagsStatic(g.vocabularySymbol(st.Name, f.Name, "tags"), f.Tags) + g.pf("static const TableFieldInfo %s[] = {\n", g.armDescriptorSymbol(un.Name, v.Name)) + member := "as." + v.Name + if armCompanioned(v) { + member += ".value" + } + g.emitFieldDescriptorAt(st, &f, "", member) + g.pf("};\n") + } +} + +func (g *tableGen) armDescriptorSymbol(owner, arm string) string { + return g.sym(owner, "arm_field_"+arm) +} diff --git a/internal/codegen/ctable/view.go b/internal/codegen/ctable/view.go new file mode 100644 index 000000000..cfe8983c3 --- /dev/null +++ b/internal/codegen/ctable/view.go @@ -0,0 +1,486 @@ +package ctable + +// UnitView is a separately linked registry over every declared type, including +// types no table reaches. It reuses the table descriptors without adding wire +// identities or JSON codecs to those outside declarations (SPEC-TABLES section 8). +import ( + "fmt" + "math/big" + "sort" + "strings" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +type viewGen struct { + unit *ir.Unit + reached map[string]bool + outside []*ir.Struct +} + +func viewBase(pkg string) string { + if pkg == "" { + return "View" + } + return strings.ToUpper(pkg[:1]) + pkg[1:] + "View" +} +func generateViewFiles(u *ir.Unit, closure map[string]bool, variable, targets map[string]bool, anyKeyed, anyList, anyMap bool) map[string][]byte { + v := &viewGen{unit: u, reached: ir.TableClosureVocabulary(u)} + for _, f := range u.Files { + for _, d := range f.Decls { + if st, ok := d.(*ir.Struct); ok && !st.IsTable && !closure[st.Name] { + v.outside = append(v.outside, st) + } + } + } + sort.Slice(v.outside, func(i, j int) bool { return v.outside[i].Name < v.outside[j].Name }) + base := viewBase(u.Package) + g := &tableGen{unit: u, file: &ir.File{Base: base}, anyVariable: len(variable) > 0, anyKeyed: anyKeyed, anyList: anyList, anyMap: anyMap, anySequence: anyList || anyMap, variable: variable, targets: targets, includes: map[string]bool{}} + banner := "/* Code generated by the schema compiler. DO NOT EDIT.\n SPDX-License-Identifier: NONE — this generated output is yours. */\n" + var h strings.Builder + h.WriteString(banner) + guard := "SCHEMA_" + strings.ToUpper(u.Package) + "_VIEW_H" + fmt.Fprintf(&h, "#ifndef %s\n#define %s\n#include \n#include \n#include \n", guard, guard) + for _, f := range u.Files { + fmt.Fprintf(&h, "#include \"%s.h\"\n", f.Base) + } + if len(u.Tables) > 0 { + for _, f := range u.Files { + fmt.Fprintf(&h, "#include \"%sTable.h\"\n", f.Base) + } + } else { + h.WriteString(tablePrimitives(u.Package, false, false, "\n")) + } + h.WriteString("\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n") + h.WriteString(viewRecords) + g.emitTableResetDeclarations(v.outside) + for _, st := range v.outside { + g.emitTableReset(st) + g.pf("extern const TableTypeInfo %s;\n", g.sym(st.Name, "info")) + g.pf("static SCHEMA_UNUSED const TableTypeInfo * %s(void) { return &%s; }\n", g.api(st.Name, "table_type"), g.sym(st.Name, "info")) + } + h.WriteString(g.body.String()) + g.body.Reset() + fmt.Fprintf(&h, "const UnitViewInfo * %s(void);\nstatic SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return %s(); }\n", g.sym("unit", "view"), g.sym("unit", "view")) + h.WriteString("#ifdef __cplusplus\n}\n#endif\n#endif\n") + var c strings.Builder + c.WriteString(banner) + fmt.Fprintf(&c, "#include \"%s.h\"\n", base) + g.outside = true + for _, st := range v.outside { + g.emitTableDescriptor(st) + } + g.outside = false + _, _, unions := v.vocabularies() + for _, un := range unions { + g.emitUnionArmDescriptors(un) + } + g.outside = false + v.emitRegistry(g) + c.WriteString(g.body.String()) + return map[string][]byte{base + ".h": []byte(h.String()), base + ".c": []byte(c.String())} +} + +const viewRecords = `typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the ` + "`///`" + ` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a ` + "`type`" + ` or a ` + "`table`" + ` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a ` + "`type`" + ` or + // a ` + "`table`" + `, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the ` + "`///`" + ` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the ` + "`field`" + ` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own ` + "`///`" + ` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared ` + "`table`" + ` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the ` + "`///`" + ` block above it; "" when none. The + // same text ` + "`type->doc`" + ` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list ` + "`type`" + ` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +` + +// emitRegistry emits UnitView() and the six sets it answers with. The data is +// a function-local static, exactly as a descriptor's is: one instance per +// process, ODR-safe, and no mutable state on the surface a caller sees. +func (v *viewGen) emitRegistry(g *tableGen) { + u := v.unit + g.pf("const UnitViewInfo * %s(void)\n{\n", g.sym("unit", "view")) + + enums, flags, unions := v.vocabularies() + var enumRows, flagRows, unionRows []viewVocab + for _, e := range enums { + v.emitEnumVocabulary(g, e) + enumRows = append(enumRows, enumVocab{e}) + } + for _, f := range flags { + v.emitFlagsVocabulary(g, f) + flagRows = append(flagRows, flagsVocab{f}) + } + for _, un := range unions { + v.emitUnionVocabulary(g, un) + unionRows = append(unionRows, unionVocab{un}) + } + v.emitVocabularyArray(g, "enums", enumRows) + v.emitVocabularyArray(g, "flags_decls", flagRows) + v.emitVocabularyArray(g, "unions", unionRows) + + types, tables := v.declarations() + v.emitTypeArray(g, "types", types) + v.emitTypeArray(g, "tables", tables) + v.emitConstants(g) + + g.pf(" static const UnitViewInfo info = { \"%s\", 0x%016xull,\n", u.Package, u.ProtocolId) + g.pf(" %s,\n", countAndData(len(types), "types")) + g.pf(" %s,\n", countAndData(len(tables), "tables")) + g.pf(" %s,\n", countAndData(len(enums), "enums")) + g.pf(" %s,\n", countAndData(len(flags), "flags_decls")) + g.pf(" %s,\n", countAndData(len(unions), "unions")) + g.pf(" %s };\n", countAndData(len(v.constants()), "constants")) + g.pf(" return &info;\n}\n") +} + +func countAndData(n int, name string) string { + if n == 0 { + return "0, NULL" + } + return fmt.Sprintf("%d, %s", n, name) +} + +// ---- the vocabulary rows ---- + +// emitEnumVocabulary emits one enum's variant rows: row 0 is None with the +// reserved id, then the variants in DECLARED order with the wire id each +// rides under (docs/SPEC-TABLES.md §8.3). +func (v *viewGen) emitEnumVocabulary(g *tableGen, e *ir.Enum) { + for i := range e.Variants { + g.emitTagsStatic(variantTagsName(e.Name, i), e.VariantTags[i]) + } + g.emitTagsStatic(declTagsName(e.Name), e.Tags) + g.pf(" static const ViewVariant %s_view_variants[] = {\n", e.Name) + g.pf(" { 0, \"None\", 0, NULL, NULL, NULL, %s },\n", annotationColumns("", nil, "")) + for i, name := range e.Variants { + id := uint64(0) + if v.reached[e.Name] { + id = ir.TableWireId(e.VariantWireName(i)) + } + g.pf(" { %d, \"%s\", 0x%016xull, NULL, NULL, NULL, %s },\n", + i+1, name, id, annotationColumns(e.VariantDocs[i], e.VariantTags[i], variantTagsName(e.Name, i))) + } + g.pf(" };\n") +} + +// emitFlagsVocabulary emits one flags declaration's rows. A row's value is a +// BIT INDEX and its id is the reserved id, because a mask's variants ride by +// position and have no per-variant wire id (§4, §8.3). +func (v *viewGen) emitFlagsVocabulary(g *tableGen, fl *ir.Flags) { + for i := range fl.Variants { + g.emitTagsStatic(variantTagsName(fl.Name, i), fl.VariantTags[i]) + } + g.emitTagsStatic(declTagsName(fl.Name), fl.Tags) + g.pf(" static const ViewVariant %s_view_variants[] = {\n", fl.Name) + for i, name := range fl.Variants { + g.pf(" { %d, \"%s\", 0, NULL, NULL, NULL, %s },\n", + i, name, annotationColumns(fl.VariantDocs[i], fl.VariantTags[i], variantTagsName(fl.Name, i))) + } + g.pf(" };\n") +} + +// emitUnionVocabulary emits one union's rows: tag 0, the empty arm, then the +// arms in declared order (SPEC §4.8). An arm naming a record carries that +// payload's descriptor; an arm that names none carries the FIELD descriptor a +// field of that type would have, emitted through the ONE field-row emitter so +// an arm's descriptor can never drift from a field's (§8.1, §8.3). +func (v *viewGen) emitUnionVocabulary(g *tableGen, un *ir.Union) { + was := g.outside + g.outside = !v.reached[un.Name] + defer func() { g.outside = was }() + for i := range un.Variants { + g.emitTagsStatic(variantTagsName(un.Name, i), un.Variants[i].Tags) + } + g.emitTagsStatic(declTagsName(un.Name), un.Tags) + g.pf(" static const ViewVariant %s_view_variants[] = {\n", un.Name) + g.pf(" { 0, \"None\", 0, NULL, NULL, NULL, %s },\n", annotationColumns("", nil, "")) + for i, va := range un.Variants { + id := uint64(0) + if v.reached[un.Name] { + id = ir.TableWireId(va.WireName()) + } + payloadName, payload, field := "NULL", "NULL", "NULL" + switch { + case va.Void(): + // a PAYLOAD-FREE arm's name and its tag are all there is of it + case va.Body(): + payloadName = fmt.Sprintf("%q", ir.FieldTypeSpelling(va.F)) + payload = "&" + g.sym(va.Type, "info") + default: + payloadName = fmt.Sprintf("%q", ir.FieldTypeSpelling(va.F)) + field = g.armDescriptorSymbol(un.Name, va.Name) + } + g.pf(" { %d, \"%s\", 0x%016xull, %s, %s, %s, %s },\n", + i+1, va.Name, id, payloadName, payload, field, + annotationColumns(va.Doc, va.Tags, variantTagsName(un.Name, i))) + } + g.pf(" };\n") +} + +// emitVocabularyArray emits one of the three vocabulary sets, in declaration +// NAME order (docs/SPEC-TABLES.md §8.3). +func (v *viewGen) emitVocabularyArray(g *tableGen, name string, decls []viewVocab) { + if len(decls) == 0 { + return + } + g.pf(" static const ViewVocabulary %s[] = {\n", name) + for _, d := range decls { + g.pf(" { \"%s\", \"%s\", %d, %d, %d, %s, %s },\n", + d.Name(), v.file(d.Name()), d.Max(), d.StorageBits(), d.NumVariants(), d.Name()+"_view_variants", + annotationColumns(d.Doc(), d.Tags(), declTagsName(d.Name()))) + } + g.pf(" };\n") +} + +// emitTypeArray emits the `types` or the `tables` set: each entry's own doc +// and tags beside the §8.1 descriptor its properties are walked through. +func (v *viewGen) emitTypeArray(g *tableGen, name string, decls []*ir.Struct) { + if len(decls) == 0 { + return + } + for _, st := range decls { + g.emitTagsStatic(declTagsName(st.Name), st.Tags) + } + g.pf(" static const ViewType %s[] = {\n", name) + for _, st := range decls { + g.pf(" { \"%s\", \"%s\", %s, &%s, %s },\n", + st.Name, v.file(st.Name), boolC(st.IsTable), g.sym(st.Name, "info"), + annotationColumns(st.Doc, st.Tags, declTagsName(st.Name))) + } + g.pf(" };\n") +} + +// emitConstants emits the one declaration kind with no runtime surface of its +// own: its name, its declared storage and its folded value (§8.3). +func (v *viewGen) emitConstants(g *tableGen) { + consts := v.constants() + if len(consts) == 0 { + return + } + for _, c := range consts { + g.emitTagsStatic(declTagsName(c.Name), c.Tags) + } + g.pf(" static const ViewConstant constants[] = {\n") + for _, c := range consts { + intValue, floatValue := "0", "0.0" + if c.IsFloat { + floatValue = formatFloat(c.Float, false) + } else { + intValue = constantIntValue(c.Int) + } + g.pf(" { \"%s\", \"%s\", \"%s\", %s, %s, %s, %s },\n", + c.Name, v.file(c.Name), c.Storage, boolC(c.IsFloat), intValue, floatValue, + annotationColumns(c.Doc, c.Tags, declTagsName(c.Name))) + } + g.pf(" };\n") +} + +// constantIntValue renders a folded integer for the int64 column. A uint64 +// declaration past the signed maximum is spelled as the two's-complement +// pattern the column holds rather than truncated silently. +func constantIntValue(value *big.Int) string { + if value == nil { + return "0" + } + if value.IsInt64() { + if value.Int64() == -1<<63 { + return "(-INT64_C(9223372036854775807)-1)" + } + return "INT64_C(" + value.String() + ")" + } + return fmt.Sprintf("(int64_t) 0x%016xull", value.Uint64()) +} + +// ---- the sets, and the order they are listed in ---- + +// viewVocab is the one shape the three vocabulary declarations share, so the +// set emitter is written once. +type viewVocab interface { + Name() string + Max() int64 + StorageBits() int + NumVariants() int + Doc() string + Tags() []string +} + +type enumVocab struct{ e *ir.Enum } + +func (d enumVocab) Name() string { return d.e.Name } +func (d enumVocab) Max() int64 { return d.e.Max } +func (d enumVocab) StorageBits() int { return d.e.StorageBits } +func (d enumVocab) NumVariants() int { return len(d.e.Variants) + 1 } // row 0 is None +func (d enumVocab) Doc() string { return d.e.Doc } +func (d enumVocab) Tags() []string { return d.e.Tags } + +type flagsVocab struct{ f *ir.Flags } + +func (d flagsVocab) Name() string { return d.f.Name } + +// Max is the highest declared BIT INDEX, the same number a flags field's +// descriptor carries in enum_max (docs/SPEC-TABLES.md §8.1). +func (d flagsVocab) Max() int64 { return int64(len(d.f.Variants) - 1) } + +// StorageBits is the mask's storage width, the smallest of 8/16/32/64 that +// holds every declared bit. +func (d flagsVocab) StorageBits() int { + for _, w := range []int{8, 16, 32, 64} { + if d.f.WireBits <= w { + return w + } + } + return 64 +} +func (d flagsVocab) NumVariants() int { return len(d.f.Variants) } +func (d flagsVocab) Doc() string { return d.f.Doc } +func (d flagsVocab) Tags() []string { return d.f.Tags } + +type unionVocab struct{ u *ir.Union } + +func (d unionVocab) Name() string { return d.u.Name } +func (d unionVocab) Max() int64 { return d.u.Max } +func (d unionVocab) StorageBits() int { return d.u.StorageBits } +func (d unionVocab) NumVariants() int { return len(d.u.Variants) + 1 } // tag 0 is the empty arm +func (d unionVocab) Doc() string { return d.u.Doc } +func (d unionVocab) Tags() []string { return d.u.Tags } + +// vocabularies returns the three vocabulary sets in declaration-NAME order. +func (v *viewGen) vocabularies() (enums []*ir.Enum, flags []*ir.Flags, unions []*ir.Union) { + for _, e := range v.unit.Enums { + enums = append(enums, e) + } + for _, f := range v.unit.Flags { + flags = append(flags, f) + } + for _, un := range v.unit.Unions { + unions = append(unions, un) + } + // a union with a table arm is a declaration like any other: it lives + // beside Unions in the IR because it has no packet wire, and the registry + // lists what was declared (docs/SPEC-TABLES.md §2.6) + for _, un := range v.unit.TableUnions { + unions = append(unions, un) + } + sort.Slice(enums, func(i, j int) bool { return enums[i].Name < enums[j].Name }) + sort.Slice(flags, func(i, j int) bool { return flags[i].Name < flags[j].Name }) + sort.Slice(unions, func(i, j int) bool { return unions[i].Name < unions[j].Name }) + return enums, flags, unions +} + +// declarations returns the `type` set and the `table` set, each in +// declaration-NAME order. A map's GENERATED entry table is not among them: it +// is claimed rather than declared (§2.8), and a walker meets it through the +// map field's own `table` column. +func (v *viewGen) declarations() (types, tables []*ir.Struct) { + for _, f := range v.unit.Files { + for _, d := range f.Decls { + if st, ok := d.(*ir.Struct); ok && !st.IsTable { + types = append(types, st) + } + } + for _, st := range f.Tables { + if st.MapEntryOf == "" { + tables = append(tables, st) + } + } + } + byName := func(s []*ir.Struct) { sort.Slice(s, func(i, j int) bool { return s[i].Name < s[j].Name }) } + byName(types) + byName(tables) + return types, tables +} + +// constants returns the unit's constants in declaration-NAME order. +func (v *viewGen) constants() []*ir.Const { + out := make([]*ir.Const, 0, len(v.unit.Consts)) + for _, c := range v.unit.Consts { + out = append(out, c) + } + sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name }) + return out +} + +// file names the schema file a declaration was declared in, by basename. +func (v *viewGen) file(name string) string { + if base, ok := v.unit.DeclFile[name]; ok { + return base + ".schema" + } + return "" +} + +func declTagsName(name string) string { return name + "_view_tags" } + +func variantTagsName(owner string, index int) string { + return fmt.Sprintf("%s_view_variant%d_tags", owner, index) +} diff --git a/internal/codegen/ctable/wide.go b/internal/codegen/ctable/wide.go new file mode 100644 index 000000000..edc6a8f75 --- /dev/null +++ b/internal/codegen/ctable/wide.go @@ -0,0 +1,125 @@ +package ctable + +import ( + "fmt" + "math/big" + "strings" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +// C's 128-bit storage is serialize.c's two explicit lanes on every host. +// No native integer extension or host byte order enters the wire. +func unitHasWideStorage(u *ir.Unit) bool { + names := ir.TableClosure(u) + for name := range ir.TableClosureVocabulary(u) { + names[name] = true + } + for name := range names { + un := u.TableUnions[name] + if un == nil { + un = u.Unions[name] + } + if un != nil { + for _, v := range un.Variants { + if !v.Void() && v.F.Type.Width == 128 { + return true + } + } + } + st := u.Tables[name] + if st == nil { + st = u.Structs[name] + } + if st == nil { + continue + } + for _, f := range st.Fields { + if f.Type.Width == 128 { + return true + } + } + } + return false +} + +func wideLanes(v *big.Int) (*big.Int, *big.Int) { + n := new(big.Int).Set(v) + if n.Sign() < 0 { + n.Add(n, new(big.Int).Lsh(big.NewInt(1), 128)) + } + mask := new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 64), big.NewInt(1)) + return new(big.Int).And(n, mask), new(big.Int).Rsh(n, 64) +} + +func tableWideLit(v *big.Int, signed bool) string { + lo, hi := wideLanes(v) + prefix := "serialize_uint128" + if signed { + prefix = "serialize_int128" + } + return fmt.Sprintf("%s_make( %sull, %sull )", prefix, hi, lo) +} + +func byteLiterals(b []byte) string { + parts := make([]string, len(b)) + for i, v := range b { + parts[i] = fmt.Sprintf("0x%02x", v) + } + return strings.Join(parts, ", ") +} + +func (g *tableGen) wireDefaultEquals(f *ir.Field, expr string) string { + if f.Type.Pointer { + return expr + ".value == 0" + } + if un, ok := f.Type.Ref.(*ir.Union); ok { + return expr + ".type == " + enumNoneConst(un.Name+"Type") + } + if f.Type.Width == 128 { + prefix := "serialize_uint128" + if f.Type.Signed { + prefix = "serialize_int128" + } + return fmt.Sprintf("%s_equal( %s, %s )", prefix, expr, g.fieldDefaultExpr(f)) + } + return expr + " == " + g.fieldDefaultExpr(f) +} + +func (g *tableGen) wireBytesDiffer(f *ir.Field, expr string) string { + if len(f.DefBytes) == 0 { + return expr + "_length != 0" + } + g.pf(" static const uint8_t initial[] = { %s };\n", byteLiterals(f.DefBytes)) + return fmt.Sprintf("%s_length != %d || memcmp( %s, initial, %d ) != 0", expr, len(f.DefBytes), expr, len(f.DefBytes)) +} + +func (g *tableGen) wireWideRead(f *ir.Field, source int, dst, rdr, ind, onBad string) { + width := tableKindWidth(source) + typ := g.cFieldType(f.Type) + g.pf("%sif ( !table_reader_has( &%s, %d ) ) { %s }\n", ind, rdr, width, onBad) + g.pf("%s%s decoded_wide;\n", ind, typ) + switch { + case width == 16: + g.pf("%sdecoded_wide.lo = table_reader_get64( &%s ); decoded_wide.hi = table_reader_get64( &%s );\n", ind, rdr, rdr) + case ir.TableKindSigned(source): + g.pf("%sint64_t narrow = (int%d_t) %s( &%s );\n", ind, width*8, tableGet(width), rdr) + g.pf("%sdecoded_wide.lo = (uint64_t) narrow; decoded_wide.hi = narrow < 0 ? UINT64_MAX : 0;\n", ind) + default: + g.pf("%sdecoded_wide.lo = %s( &%s ); decoded_wide.hi = 0;\n", ind, tableGet(width), rdr) + } + if lo, hi, ok := ir.TableRawRange(f); ok { + for _, bound := range []struct { + v *big.Int + op string + }{{lo, "<"}, {hi, ">"}} { + g.pf("%s{\n%s %s bound = %s;\n", ind, ind, typ, tableWideLit(bound.v, f.Type.Signed)) + cmp := fmt.Sprintf("decoded_wide.hi %s bound.hi || (decoded_wide.hi == bound.hi && decoded_wide.lo %s bound.lo)", bound.op, bound.op) + if f.Type.Signed { + cmp = fmt.Sprintf("serialize_int128_compare( decoded_wide, bound ) %s 0", bound.op) + } + g.pf("%s if ( %s ) { decoded_wide = bound; r->report->clamped++; }\n%s}\n", ind, cmp, ind) + } + } + g.pf("%s%s = decoded_wide;\n", ind, dst) +} diff --git a/internal/codegen/ctable/wire.go b/internal/codegen/ctable/wire.go new file mode 100644 index 000000000..e86f27421 --- /dev/null +++ b/internal/codegen/ctable/wire.go @@ -0,0 +1,509 @@ +package ctable + +// The file wire, SPEC-TABLES section 3: full identities, first-use references +// and flat node records for the variable class. +import ( + "fmt" + "strconv" + "strings" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +func (g *tableGen) fileWire() bool { return true } + +func (g *tableGen) wirePrimitives() string { + runtime := "" + if g.fileWire() { + runtime = strings.ReplaceAll(fileWireRuntime, "@CAPACITY@", strconv.Itoa(len(ir.TableWireIds(g.unit))+1)) + runtime = strings.ReplaceAll(runtime, "@INLINE@", tableInlineMacro(g.unit.Package)) + slots := 2 + for slots < 2*(len(ir.TableWireIds(g.unit))+1) { + slots *= 2 + } + runtime = strings.ReplaceAll(runtime, "@ID_SLOTS@", strconv.Itoa(slots)) + writeNodes, readNodes := "", "" + if g.anyVariable { + writeNodes = " struct TableNumbering * nodes;" + readNodes = " struct TableNodeMap * nodes;" + } + runtime = strings.ReplaceAll(runtime, "@WRITE_NODES@", writeNodes) + runtime = strings.ReplaceAll(runtime, "@READ_NODES@", readNodes) + } + return tablePrimitives(g.unit.Package, g.anyVariable, g.anyKeyed, runtime) + tableTextRuntime +} + +const fileWireRuntime = ` +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[@CAPACITY@]; + uint32_t slots[@ID_SLOTS@], positions[@CAPACITY@]; + int count; +} TableWriteIds; + +typedef struct TableWriter +{ + uint8_t * buffer; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; +@WRITE_NODES@ +} TableWriter; + +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) +{ + TableWriter w; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); + return w; +} +static SCHEMA_UNUSED @INLINE@ void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) +{ + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } + w->offset += bytes; +} +static SCHEMA_UNUSED @INLINE@ void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } +static SCHEMA_UNUSED @INLINE@ void table_writer_put16( TableWriter * w, uint16_t v ) +{ + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); +} +static SCHEMA_UNUSED @INLINE@ void table_writer_put32( TableWriter * w, uint32_t v ) +{ + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); +} +static SCHEMA_UNUSED @INLINE@ void table_writer_put64( TableWriter * w, uint64_t v ) +{ + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (@ID_SLOTS@-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(@ID_SLOTS@-1); + } + if(v->count==@CAPACITY@) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } +} +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) +{ + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); +} + +typedef struct TableReader +{ + const uint8_t * buffer; + int64_t size, offset; + TableReport * report; + const uint8_t * ids; +@READ_NODES@ + uint64_t id_count; + int nested; +} TableReader; + +static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) +{ + TableReader r; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; + return r; +} +static SCHEMA_UNUSED @INLINE@ int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED @INLINE@ int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } +static SCHEMA_UNUSED @INLINE@ uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } +static SCHEMA_UNUSED @INLINE@ uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED @INLINE@ uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED @INLINE@ uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) +{ + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); +} +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) +{ + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; +} +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) +{ + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; +} +static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) +{ + int width = 0; uint64_t n; + switch ( kind ) + { + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } + if ( !table_reader_has( r, 1 ) ) { return 0; } + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; +} +` + +func (g *tableGen) wireEnumWrite(e *ir.Enum, expr, ind string) { + g.pf("%sswitch ( %s )\n%s{\n", ind, expr, ind) + g.pf("%s case %s: table_writer_leb( w, 0 ); break;\n", ind, enumNoneConst(e.Name)) + for i, v := range e.Variants { + g.pf("%s case %s: table_writer_id( w, 0x%016xull ); break;\n", ind, enumConst(e.Name, v), ir.TableWireId(e.VariantWireName(i))) + } + g.pf("%s default: return 0; /* no declared identity */\n%s}\n", ind, ind) +} + +func (g *tableGen) wireScalarWrite(f *ir.Field, expr, ind string) { + if e := enumRef(f); e != nil { + g.wireEnumWrite(e, expr, ind) + return + } + if tableKindWidth(ir.TableWireScalarKind(f)) == 16 { + g.pf("%stable_writer_put64( w, (%s).lo ); table_writer_put64( w, (%s).hi );\n", ind, expr, expr) + return + } + g.emitTableWriteElement(f, ir.TableWireScalarKind(f), expr, ind) +} + +// Measurement visits a frame once and adds the length prefix afterwards: no +// bytes are emitted in this mode. Save probes with that same measure +// before writing the prefix, so recursive frames never double recursively. +func (g *tableGen) wireFrame(call, ind string) { + g.pf("%sif ( w->buffer == NULL )\n%s{\n", ind, ind) + g.pf("%s int64_t frame_begin = w->offset;\n", ind) + g.pf("%s if ( !%s ) { return 0; }\n", ind, call) + g.pf("%s table_writer_leb( w, (uint64_t)(w->offset - frame_begin) );\n%s}\n%selse\n%s{\n", ind, ind, ind, ind) + g.pf("%s TableWriter probe = table_writer_probe( w );\n", ind) + g.pf("%s if ( !%s ) { return 0; }\n", ind, strings.ReplaceAll(call, "( w,", "( &probe,")) + g.pf("%s table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset );\n", ind) + g.pf("%s if ( !%s ) { return 0; }\n%s}\n", ind, call, ind) +} + +func (g *tableGen) wirePayload(f *ir.Field, expr, ind string) { + if f.Type.Pointer { + g.pf("%s{ const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&%s); uint64_t index=table_number_find(w->nodes,node);\n", ind, expr) + g.pf("%s if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); }\n", ind) + return + } + kind := ir.TableWireScalarKind(f) + switch kind { + case tkTable: + g.wireFrame(fmt.Sprintf("%s( w, &%s )", g.api(f.Type.Name, "save_body"), expr), ind) + case tkUnion: + g.pf("%sif ( !%s( w, &%s ) ) { return 0; }\n", ind, g.unionWireName(f.Type.Ref.(*ir.Union), "save"), expr) + default: + g.wireScalarWrite(f, expr, ind) + } +} + +func (g *tableGen) wireFieldHelper(st *ir.Struct, f *ir.Field) string { + return g.sym(st.Name, "wire_"+f.Name) +} + +func (g *tableGen) emitWireFieldHelper(st *ir.Struct, f *ir.Field) { + expr := "value->" + f.Name + kind := ir.TableWireScalarKind(f) + g.pf("static SCHEMA_UNUSED int %s( TableWriter * w, const %s * value )\n{\n", g.wireFieldHelper(st, f), st.Name) + if (f.Type.Kind == ir.TString && !f.Type.Blob()) || f.Type.Kind == ir.TWString || (f.Type.Kind == ir.TBytes && !f.Type.Blob()) { + g.pf(" if ( %s_length < 0 || %s_length > %d ) { return 0; }\n", expr, expr, f.Type.Size) + } else if f.Array == ir.ArrayCounted { + g.pf(" if ( %s_count < 0 || %s_count > %d ) { return 0; }\n", expr, expr, f.ArrayBound) + } + switch { + case f.IsList() || f.IsMap(): + g.emitSequenceWrite(f, expr) + case f.Type.Kind == ir.TWString: + g.pf(" int32_t i; for ( i = 0; i < %s_length; i++ ) { table_writer_put16( w, %s[i] ); }\n", expr, expr) + case (f.Type.Kind == ir.TString && !f.Type.Blob()): + g.pf(" table_writer_raw( w, %s, %s_length );\n", expr, expr) + case (f.Type.Kind == ir.TBytes && !f.Type.Blob()): + g.pf(" table_writer_put8( w, %d ); table_writer_leb( w, (uint64_t) %s_length );\n", tkU8, expr) + g.pf(" table_writer_raw( w, %s, %s_length );\n", expr, expr) + case f.KeyEnum != "": + g.pf(" uint64_t count = 0; int32_t i;\n") + g.pf(" for ( i = 0; i < %s; i++ )\n {\n", enumMaxConst(f.KeyEnum)) + g.retainIndex(f, "i", " ") + g.wireKeyedRides(f, " ") + g.pf(" count++;\n }\n table_writer_put8( w, %d ); table_writer_leb( w, count );\n", kind) + g.pf(" for ( i = 0; i < %s; i++ )\n {\n", enumMaxConst(f.KeyEnum)) + g.retainIndex(f, "i", " ") + g.wireKeyedRides(f, " ") + g.wireEnumWrite(f.KeyEnumRef, "i + 1", " ") + if kind == tkTable { + g.wireFrame(fmt.Sprintf("%s( w, &%s[i] )", g.api(f.Type.Name, "save_body"), expr), " ") + } else { + g.pf(" {\n TableWriter probe = table_writer_probe( w );\n TableWriter * outer = w;\n w = &probe;\n") + g.wireScalarWrite(f, expr+"[i]", " ") + g.pf(" w = outer; table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset );\n") + g.wireScalarWrite(f, expr+"[i]", " ") + g.pf(" }\n") + } + g.pf(" }\n") + case f.Array != ir.ArrayNone: + n := strconv.FormatInt(f.ArrayBound, 10) + if f.Array == ir.ArrayCounted { + n = expr + "_count" + } + g.pf(" int32_t i;\n table_writer_put8( w, %d ); table_writer_leb( w, (uint64_t) %s );\n", kind, n) + g.pf(" for ( i = 0; i < %s; i++ )\n {\n", n) + g.retainIndex(f, "i", " ") + g.wirePayload(f, expr+"[i]", " ") + g.pf(" }\n") + default: + panic("wire field helper on unframed field") + } + g.pf(" return !w->overflow;\n}\n\n") +} + +func (g *tableGen) wireKeyedRides(f *ir.Field, ind string) { + g.retainIndex(f, "i", ind) + expr := "value->" + f.Name + "[i]" + if ir.TableWireScalarKind(f) == tkTable { + g.pf("%sTableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1;\n", ind) + g.pf("%sif ( !%s( &slot_probe, &%s ) ) { return 0; }\n", ind, g.api(f.Type.Name, "save_body"), expr) + g.pf("%stable_writer_rewind(&slot_probe);\n%sif ( slot_probe.offset == 1 ) { continue; }\n", ind, ind) + } else { + g.pf("%sif ( %s ) { continue; }\n", ind, g.wireDefaultEquals(f, expr)) + } +} + +func (g *tableGen) emitWireWrite(st *ir.Struct) { + for _, f := range st.Fields { + if f.IsMap() || f.Array != ir.ArrayNone || (f.Type.Kind == ir.TBytes && !f.Type.Blob()) || ((f.Type.Kind == ir.TString && !f.Type.Blob()) || f.Type.Kind == ir.TWString) { + g.emitWireFieldHelper(st, f) + } + } + g.pf("static SCHEMA_UNUSED int %s( TableWriter * w, const %s * value )\n{\n", g.api(st.Name, "save_body"), st.Name) + g.pf(" (void) value;\n") + if g.retain { + g.pf(" TableRetainWalk body_keep=retention;\n") + } + guards := tableGuardExprs(st) + for ordinal, f := range st.Fields { + _ = ordinal + expr := "value->" + f.Name + kind := ir.TableWireScalarKind(f) + wireKind := kind + framed := f.IsMap() || f.Array != ir.ArrayNone || (f.Type.Kind == ir.TBytes && !f.Type.Blob()) || ((f.Type.Kind == ir.TString && !f.Type.Blob()) || f.Type.Kind == ir.TWString) + if f.IsMap() || f.Array != ir.ArrayNone || (f.Type.Kind == ir.TBytes && !f.Type.Blob()) { + wireKind = tkArray + } + if f.KeyEnum != "" { + wireKind = tkKeyed + } + g.pf(" { /* %s */\n", f.Name) + if g.retain { + g.pf(" retention=table_retain_step(body_keep,%d,0);\n", ordinal) + } + if guard := guards[f.Name]; guard != "" { + g.pf(" if ( %s )\n {\n", guard) + } + condition := "!( " + g.wireDefaultEquals(f, expr) + " )" + switch { + case f.IsList() || f.IsMap(): + g.pf(" if(%s.count<0) { return 0; }\n", expr) + condition = expr + ".count > 0" + case f.Type.Optional: + condition = expr + "_present" + case (f.Type.Kind == ir.TBytes && !f.Type.Blob()) || ((f.Type.Kind == ir.TString && !f.Type.Blob()) || f.Type.Kind == ir.TWString): + g.pf(" if ( %s_length < 0 || %s_length > %d ) { return 0; }\n", expr, expr, f.Type.Size) + condition = g.wireBytesDiffer(f, expr) + case f.Array == ir.ArrayCounted: + g.pf(" if ( %s_count < 0 || %s_count > %d ) { return 0; }\n", expr, expr, f.ArrayBound) + condition = expr + "_count > 0" + case f.KeyEnum != "": + g.pf(" int rides = 0; int32_t i;\n for ( i = 0; i < %s; i++ )\n {\n", enumMaxConst(f.KeyEnum)) + g.wireKeyedRides(f, " ") + g.pf(" rides = 1; break;\n }\n") + condition = "rides" + case f.Array == ir.ArrayFixed: + if kind == tkTable { + condition = "1" + } else { + g.pf(" int rides = 0; int32_t i;\n for ( i = 0; i < %d; i++ ) { if ( !( %s ) ) { rides = 1; break; } }\n", f.ArrayBound, g.wireDefaultEquals(f, expr+"[i]")) + condition = "rides" + } + case kind == tkTable: + g.pf(" TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1;\n if ( !%s( &default_probe, &%s ) ) { return 0; }\n", g.api(f.Type.Name, "save_body"), expr) + g.pf(" table_writer_rewind(&default_probe);\n") + condition = "default_probe.offset > 1" + case kind == tkUnion: + condition = expr + ".type != " + enumConst(f.Type.Name+"Type", "None") + } + if f.Type.Optional { + condition = expr + "_present" + } + g.pf(" if ( %s )\n {\n", condition) + g.pf(" if ( w->check_default ) { w->offset = 2; return 1; }\n") + g.pf(" table_writer_id( w, 0x%016xull ); table_writer_put8( w, %d );\n", ir.TableFieldWireId(f), wireKind) + switch { + case framed: + g.wireFrame(fmt.Sprintf("%s( w, value )", g.wireFieldHelper(st, f)), " ") + + default: + g.wirePayload(f, expr, " ") + } + g.pf(" }\n") + if guards[f.Name] != "" { + g.pf(" }\n") + } + g.pf(" }\n") + } + if g.retain { + g.pf(" retention=body_keep;if(!table_retain_tail(w,retention))return 0;\n") + } + g.pf(" table_writer_leb( w, 0 );\n return !w->overflow;\n}\n\n") + if g.retain || g.isVar(st.Name) || st.IsMapEntry() { + return + } + for _, measure := range []bool{true, false} { + verb, args, buffer, cap := "save", ", uint8_t * buffer, int64_t capacity", "buffer", "capacity" + if measure { + verb, args, buffer, cap = "measure", "", "NULL", "INT64_MAX" + } + g.pf("static SCHEMA_UNUSED int64_t %s( const %s * value%s )\n{\n", g.api(st.Name, verb), st.Name, args) + if !measure { + g.pf(" if ( buffer == NULL || capacity < 0 ) { return -1; }\n") + } + g.pf(" TableWriteIds vocabulary; TableWriter w = table_writer_make( %s, %s, &vocabulary );\n table_writer_put8( &w, 1 );\n", buffer, cap) + g.pf(" if ( !%s( &w, value ) ) { return -1; }\n table_writer_finish( &w );\n return w.overflow ? -1 : w.offset;\n}\n\n", g.api(st.Name, "save_body")) + } +} diff --git a/internal/codegen/ctable/wire_read.go b/internal/codegen/ctable/wire_read.go new file mode 100644 index 000000000..52377ab5a --- /dev/null +++ b/internal/codegen/ctable/wire_read.go @@ -0,0 +1,315 @@ +package ctable + +import ( + "fmt" + + "github.com/mas-bandwidth/schema/v2/ir" +) + +// Named element types keep their declared wire kind, including flags. +func wireElementWidenTest(f *ir.Field, kind int, expr string) string { + if f.Type.Pointer { + return "0" + } + // Named flags carry unsigned kind 9 and obey the same widening ladder. + return fmt.Sprintf("table_kind_widens(%s,%d)", expr, kind) +} + +func (g *tableGen) wireEnumRead(e *ir.Enum, dst, rdr, ind, onBad string) { + g.pf("%s{\n%s uint64_t variant_ref;\n", ind, ind) + g.pf("%s if ( !table_reader_leb( &%s, &variant_ref ) || variant_ref > %s.id_count ) { %s }\n", ind, rdr, rdr, onBad) + g.pf("%s if ( variant_ref == 0 ) { %s = %s; }\n%s else\n%s {\n", ind, dst, enumNoneConst(e.Name), ind, ind) + g.pf("%s switch ( table_reader_id_at( &%s, variant_ref ) )\n%s {\n", ind, rdr, ind) + for i, v := range e.Variants { + g.pf("%s case 0x%016xull: %s = %s; break;\n", ind, ir.TableWireId(e.VariantWireName(i)), dst, enumConst(e.Name, v)) + } + g.pf("%s default: %s = %s; "+g.unknownEvent()+" break;\n", ind, dst, enumNoneConst(e.Name)) + g.pf("%s }\n%s }\n%s}\n", ind, ind, ind) +} + +func (g *tableGen) wireScalarRead(f *ir.Field, dst, rdr, wireKind, ind, onBad string) { + if f.Type.Pointer { + g.pf("%s{ uint64_t index; if(!table_reader_leb(&%s,&index)) { %s }\n", ind, rdr, onBad) + target := ir.TableWireId(ir.PointeeWireName(f)) + if f.Type.Blob() { + target = ir.BlobWireTypeId(f) + } + g.pf("%s table_node_resolve(%s.nodes,&%s,index,UINT64_C(0x%016x),r->report); }\n", ind, rdr, dst, target) + return + } + if e := enumRef(f); e != nil { + g.wireEnumRead(e, dst, rdr, ind, onBad) + return + } + kind := ir.TableWireScalarKind(f) + if f.Type.Kind == ir.TBytes && !f.Type.Blob() { + kind = tkU8 + } + // The caller has already accepted this kind or a precise widening. Keep + // each numeric decode at the source width, including its range clamps. + g.pf("%sswitch ( %s )\n%s{\n", ind, wireKind, ind) + for source := 1; source <= 29; source++ { + if source != kind && !ir.TableKindWidens(source, kind) { + continue + } + g.pf("%s case %d:\n%s {\n", ind, source, ind) + // onBad can contain break, which must leave the caller's element loop, + // not this dispatch switch. A per-field label supplies that boundary. + switch { + case tableKindWidth(kind) == 16: + g.wireWideRead(f, source, dst, rdr, ind+" ", onBad) + case source == kind: + g.emitTableReadScalarFrom(f, source, dst, ind+" ", rdr, onBad) + default: + g.wireWidenedScalar(f, source, dst, rdr, ind+" ", onBad) + } + g.pf("%s break;\n%s }\n", ind, ind) + } + g.pf("%s default: %s\n%s}\n", ind, onBad, ind) +} + +func (g *tableGen) wireWidenedScalar(f *ir.Field, source int, dst, rdr, ind, onBad string) { + width := tableKindWidth(source) + g.pf("%sif ( !table_reader_has( &%s, %d ) ) { %s }\n", ind, rdr, width, onBad) + if source == tkF32 { + g.pf("%sdouble decoded_wide = table_wire_widen_f32( table_reader_get32( &%s ) );\n", ind, rdr) + if f.HasFloatRange { + g.pf("%sif ( decoded_wide < %s ) { decoded_wide = %s; r->report->clamped++; }\n", ind, formatFloat(f.FMin, false), formatFloat(f.FMin, false)) + g.pf("%selse if ( decoded_wide > %s ) { decoded_wide = %s; r->report->clamped++; }\n", ind, formatFloat(f.FMax, false), formatFloat(f.FMax, false)) + } + } else { + signed := ir.TableKindSigned(source) + storage, cast := "uint64_t", fmt.Sprintf("uint%d_t", width*8) + if signed { + storage, cast = "int64_t", fmt.Sprintf("int%d_t", width*8) + } + g.pf("%s%s decoded_wide = (%s) %s( &%s );\n", ind, storage, cast, tableGet(width), rdr) + if f.HasIntRange { + low, high := tableClampEnds(f, 8) + if low { + g.pf("%sif ( decoded_wide < %s ) { decoded_wide = %s; r->report->clamped++; }\n", ind, tableIntLit(f.IntMin, signed, 8), tableIntLit(f.IntMin, signed, 8)) + } + if high { + g.pf("%sif ( decoded_wide > %s ) { decoded_wide = %s; r->report->clamped++; }\n", ind, tableIntLit(f.IntMax, signed, 8), tableIntLit(f.IntMax, signed, 8)) + } + } + if f.Type.Kind == ir.TBits && f.Type.Width < 64 { + max := (uint64(1) << f.Type.Width) - 1 + g.pf("%sif ( decoded_wide > %dull ) { decoded_wide = %dull; r->report->clamped++; }\n", ind, max, max) + } + } + g.pf("%s%s = decoded_wide;\n", ind, dst) +} + +// A builder storage-cap refusal must pass through every enclosing body without +// a default reset or an extra malformed event. +func (g *tableGen) wireRefusalCheck(ind string) { + if g.anySequence { + g.pf("%sif(r->nodes!=NULL && r->nodes->refused) { return 0; }\n", ind) + } +} + +func (g *tableGen) emitWireRead(st *ir.Struct) { + g.pf("static SCHEMA_UNUSED int %s( TableReader * r, %s * value )\n{\n", g.api(st.Name, "load_body"), st.Name) + g.wireRefusalCheck(" ") + g.pf(" %s( value );\n", g.api(st.Name, "reset")) + if g.retain { + g.pf(" TableRetainWalk body_keep=retention;table_retain_discard(retention,0,0);\n") + } + g.pf(" for ( ;; )\n {\n uint64_t ref, id; uint8_t kind;\n") + g.pf(" if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; }\n") + g.pf(" if ( ref == 0 ) { return 1; }\n") + g.pf(" if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; }\n") + g.pf(" id = table_reader_id_at( r, ref ); kind = table_reader_get8( r );\n") + g.pf(" if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; }\n") + g.pf(" switch ( id )\n {\n") + for ordinal, f := range st.Fields { + kind := ir.TableWireScalarKind(f) + wireKind := kind + if f.IsMap() || f.Array != ir.ArrayNone || (f.Type.Kind == ir.TBytes && !f.Type.Blob()) { + wireKind = tkArray + } + if f.KeyEnum != "" { + wireKind = tkKeyed + } + if f.Type.Kind == ir.TBytes && !f.Type.Blob() { + kind = tkU8 + } + plain := !f.Type.Pointer && f.Array == ir.ArrayNone && f.KeyEnum == "" && (kind == tkI16 || kind == tkI32 || kind == tkI64 || kind == 18 || kind == tkU16 || kind == tkU32 || kind == tkU64 || kind == 19 || kind == tkF64) + g.pf(" case 0x%016xull: /* %s */\n {\n", ir.TableFieldWireId(f), f.Name) + g.pf(" if ( kind != %d )\n {\n", wireKind) + if plain { + g.pf(" if ( table_kind_widens( kind, %d ) )\n {\n", wireKind) + g.wireScalarRead(f, "value->"+f.Name, "(*r)", "kind", " ", "r->report->malformed = 1; return 0;") + if !st.IsMapEntry() || f.Name != ir.MapKeyFieldName { + g.pf(" r->report->widened++;\n") + } // map key widening is counted once by the key scan + + if f.Type.Optional { + g.pf(" value->%s_present = 1;\n", f.Name) + } + g.pf(" break;\n }\n") + } + g.pf(" r->report->kind_mismatch++;\n if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; }\n break;\n }\n") + g.retainReadField(f, ordinal, " ") + g.wireReadFieldAt(f, kind, "value->"+f.Name) + if f.Type.Optional { + g.pf(" value->%s_present = 1;\n", f.Name) + } + g.pf(" break;\n }\n") + } + if g.anyVariable { + g.pf("%s", " case UINT64_MAX:\n if(r->nodes==NULL) { "+g.unknownEvent()+" }\n if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; }\n break;\n") + } + if g.retain { + g.pf(" default:\n r->report->unknown += 1;\n if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1;return 0;}break;\n }\n }\n}\n\n") + } else { + g.pf("%s", " default:\n "+g.unknownEvent()+"\n if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; }\n break;\n }\n }\n}\n\n") + } + if g.retain || g.isVar(st.Name) || st.IsMapEntry() { + return + } + g.pf("static SCHEMA_UNUSED int %s( %s * value, const uint8_t * buffer, int64_t bytes, TableReport * report )\n{\n", g.api(st.Name, "load"), st.Name) + g.pf(" TableReport ignored; TableReader r;\n memset( &ignored, 0, sizeof( ignored ) );\n if ( report == NULL ) { report = &ignored; }\n") + g.pf(" %s( value );\n if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; }\n", g.api(st.Name, "reset")) + g.pf(" return %s( &r, value );\n}\n\n", g.api(st.Name, "load_body")) +} + +func (g *tableGen) wireReadFieldAt(f *ir.Field, kind int, dst string) { + g.wireReadFieldPayload(f, kind, dst, "") +} +func (g *tableGen) wireReadFieldPayload(f *ir.Field, kind int, dst, bounded string) { + ind := " " + switch { + case f.IsMap(): + g.emitMapRead(f, dst, bounded) + case f.IsList(): + g.emitListRead(f, dst, bounded) + case f.Type.Pointer && f.Array == ir.ArrayNone: + g.wireScalarRead(f, dst, "(*r)", "kind", ind, "r->report->malformed=1; return 0;") + case f.Type.Kind == ir.TWString: + g.pf("%sTableReader sub; int64_t keep, i;\n", ind) + g.pf("%sif ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; }\n", ind) + g.pf("%sif ( sub.size %% 2 != 0 || !table_wire_utf16( sub.buffer, sub.size/2 ) ) { r->report->malformed = 1; %s[0] = 0; %s_length = 0; break; }\n", ind, dst, dst) + g.pf("%skeep = table_wire_utf16_clamp( sub.buffer, sub.size/2, %d ); if ( keep != sub.size/2 ) { r->report->clamped++; }\n", ind, f.Type.Size) + g.pf("%sfor ( i = 0; i < keep; i++ ) { %s[i] = table_wire_utf16_unit( sub.buffer, i ); } %s[keep] = 0; %s_length = (int32_t)keep;\n", ind, dst, dst, dst) + case (f.Type.Kind == ir.TString && !f.Type.Blob()): + g.pf("%sTableReader sub; uint64_t keep;\n", ind) + g.pf("%sif ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; }\n", ind) + g.pf("%sif ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) )\n%s{\n", ind, ind) + if len(f.DefBytes) != 0 { + g.pf("%s static const uint8_t initial[] = { %s };\n%s memcpy( %s, initial, sizeof( initial ) );\n", ind, byteLiterals(f.DefBytes), ind, dst) + } + g.pf("%s r->report->malformed = 1; %s[%d] = 0; %s_length = %d; break;\n%s}\n", ind, dst, len(f.DefBytes), dst, len(f.DefBytes), ind) + g.pf("%skeep = (uint64_t) sub.size;\n%sif ( keep > %d ) { keep = table_wire_utf8_clamp( sub.buffer, keep, %d ); r->report->clamped++; }\n", ind, ind, f.Type.Size, f.Type.Size) + g.pf("%smemcpy( %s, sub.buffer, (size_t) keep ); %s[keep] = 0; %s_length = (int32_t) keep;\n", ind, dst, dst, dst) + case f.KeyEnum != "": + g.wireReadKeyed(f, kind, ind) + case f.Array != ir.ArrayNone || (f.Type.Kind == ir.TBytes && !f.Type.Blob()): + bound := f.ArrayBound + count := "" + if f.Array == ir.ArrayCounted { + count = dst + "_count" + } + if f.Type.Kind == ir.TBytes && !f.Type.Blob() { + bound = f.Type.Size + count = dst + "_length" + } + g.pf("%sTableReader sub;\n", ind) + if bounded == "" { + g.pf("%sif ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; }\n", ind) + } else { + g.pf("%ssub = %s; %s.offset = %s.size;\n", ind, bounded, bounded, bounded) + } + g.pf("%sif ( sub.size >= 2 )\n%s{\n", ind, ind) + g.pf("%s uint8_t elem_kind; uint64_t count, kept, i;\n", ind) + if bounded == "" { + g.pf("%s if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; }\n%s else\n%s {\n", ind, ind, ind) + } else { + g.pf("%s elem_kind = table_reader_get8( &sub );\n", ind) + g.pf("%s if ( !table_reader_leb( &sub, &count ) ) { r->report->malformed = 1; break; }\n%s {\n", ind, ind) + } + g.pf("%s if ( elem_kind != %d )\n%s {\n", ind, kind, ind) + g.pf("%s if ( !(%s) ) { r->report->kind_mismatch++; break; }\n%s r->report->widened++;\n%s }\n", ind, wireElementWidenTest(f, kind, "elem_kind"), ind, ind) + g.retainReplace(f, ind+" ") + g.pf("%s kept = count; if ( kept > %d ) { kept = %d; r->report->clamped++; }\n", ind, bound, bound) + if f.Array == ir.ArrayCounted { + g.pf("%s int32_t previous_count = %s_count;\n", ind, dst) + } + if count != "" { + g.pf("%s %s = 0;\n", ind, count) + } + g.pf("%s for ( i = 0; i < kept; i++ )\n%s {\n", ind, ind) + g.retainIndex(f, "i", ind+" ") + bad := "r->report->malformed = 1; goto end_" + f.Name + ";" + switch kind { + case tkTable: + g.pf("%s TableReader elem;\n%s if ( !table_reader_span( &sub, &elem ) ) { %s }\n", ind, ind, bad) + g.pf("%s %s( &elem, &%s[i] );\n", ind, g.api(f.Type.Name, "load_body"), dst) + g.wireRefusalCheck(ind + " ") + g.pf("%s if(elem.offset!=elem.size) { r->report->malformed=1; %s(&%s[i]); }\n", ind, g.api(f.Type.Name, "reset"), dst) + case tkUnion: + g.pf("%s if ( !%s( &sub, &%s[i], 1 ) ) { %s }\n", ind, g.unionWireName(f.Type.Ref.(*ir.Union), "load"), dst, bad) + default: + g.wireScalarRead(f, dst+"[i]", "sub", "elem_kind", ind+" ", bad) + } + if count != "" { + g.pf("%s %s = (int32_t) i + 1;\n", ind, count) + } + g.pf("%s }\n%s end_%s: ;\n", ind, ind, f.Name) + if f.Array == ir.ArrayCounted { + g.wireCountedTailReset(f, dst, ind+" ") + } + g.pf("%s }\n%s}\n", ind, ind) + case kind == tkUnion: + g.pf("%sif ( !%s( r, &%s, 0 ) ) { return 0; }\n", ind, g.unionWireName(f.Type.Ref.(*ir.Union), "load"), dst) + + case kind == tkTable: + g.pf("%sTableReader sub;\n%sif ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; }\n", ind, ind) + g.pf("%s%s( &sub, &%s );\n", ind, g.api(f.Type.Name, "load_body"), dst) + g.wireRefusalCheck(ind) + g.pf("%sif ( sub.offset != sub.size ) { r->report->malformed = 1; %s( &%s ); }\n", ind, g.api(f.Type.Name, "reset"), dst) + default: + g.wireScalarRead(f, dst, "(*r)", "kind", ind, "r->report->malformed = 1; return 0;") + } +} + +func (g *tableGen) wireReadKeyed(f *ir.Field, kind int, ind string) { + g.pf("%sTableReader sub;\n%sif ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; }\n", ind, ind) + g.pf("%sif ( sub.size >= 2 )\n%s{\n", ind, ind) + g.pf("%s uint8_t elem_kind; uint64_t count, i;\n", ind) + g.pf("%s if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; }\n", ind) + g.pf("%s if ( elem_kind != %d ) { if ( !(%s) ) { r->report->kind_mismatch++; break; } r->report->widened++; }\n", ind, kind, wireElementWidenTest(f, kind, "elem_kind")) + g.pf("%s for ( i = 0; i < count; i++ )\n%s {\n", ind, ind) + g.pf("%s uint64_t key_ref, key; int32_t slot = -1; TableReader elem;\n", ind) + g.pf("%s if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; }\n", ind) + g.pf("%s key = table_reader_id_at( &sub, key_ref );\n%s switch ( key )\n%s {\n", ind, ind, ind) + for i := range f.KeyEnumRef.Variants { + g.pf("%s case 0x%016xull: slot = %d; break;\n", ind, ir.TableWireId(f.KeyEnumRef.VariantWireName(i)), i) + } + g.pf("%s default: "+g.unknownEvent()+" break;\n%s }\n%s if ( slot < 0 ) { continue; }\n", ind, ind, ind) + g.retainIndex(f, "slot", ind+" ") + dst := fmt.Sprintf("value->%s[slot]", f.Name) + if kind == tkTable { + g.pf("%s %s( &elem, &%s );\n", ind, g.api(f.Type.Name, "load_body"), dst) + g.wireRefusalCheck(ind + " ") + g.pf("%s if(elem.offset!=elem.size) { r->report->malformed=1; %s(&%s); }\n", ind, g.api(f.Type.Name, "reset"), dst) + } else { + g.wireScalarRead(f, dst, "elem", "elem_kind", ind+" ", "r->report->malformed = 1; goto next_"+f.Name+";") + } + if kind != tkTable { + g.pf("%s next_%s: ;\n", ind, f.Name) + } + g.pf("%s }\n%s}\n", ind, ind) +} + +// Keep value-initialized storage past a replacement's decoded prefix (#725). +func (g *tableGen) wireCountedTailReset(f *ir.Field, dst, ind string) { + g.pf("%s{ uint32_t tail; for (tail=(uint32_t)%s_count;tail<(uint32_t)previous_count && tail<%d;tail++) {\n", ind, dst, f.ArrayBound) + if st, ok := f.Type.Ref.(*ir.Struct); ok && !f.Type.Pointer { + g.pf("%s %s(&%s[tail]);\n", ind, g.api(st.Name, "reset"), dst) + } else { + g.pf("%s memset(&%s[tail],0,sizeof(%s[tail]));\n", ind, dst, dst) + } + g.pf("%s} }\n", ind) +} diff --git a/internal/goldens/goldens_test.go b/internal/goldens/goldens_test.go index a05a69545..791474fa9 100644 --- a/internal/goldens/goldens_test.go +++ b/internal/goldens/goldens_test.go @@ -334,7 +334,7 @@ func TestGoldenPacketWideSource(t *testing.T) { func TestWideTextIsRefusedByEveryOtherTarget(t *testing.T) { u := loadCorpusDir(t, corpusWideDir) for _, target := range compiler.New().Targets() { - if target == "cpp" || target == "cs" || target == "go" { + if target == "cpp" || target == "c" || target == "cs" || target == "go" { continue } if _, err := schema.Generate(u, target, nil); err == nil { diff --git a/internal/tablenames/c.go b/internal/tablenames/c.go index 20a86046c..790203b1b 100644 --- a/internal/tablenames/c.go +++ b/internal/tablenames/c.go @@ -11,7 +11,270 @@ const C Backend = 1 << 5 func init() { define(C, + Name{Name: "table_retain_known", What: "the C retention runtime"}, + Name{Name: "TableRetain", What: "the C retention runtime"}, + Name{Name: "TableRetainId", What: "the C retention runtime"}, + Name{Name: "TableRetainIds", What: "the C retention runtime"}, + Name{Name: "TableRetainIn", What: "the C retention runtime"}, + Name{Name: "TableRetainMessage", What: "the C retention runtime"}, + Name{Name: "TableRetainOut", What: "the C retention runtime"}, + Name{Name: "TableRetainPath", What: "the C retention runtime"}, + Name{Name: "TableRetainSaveNode", What: "the C retention runtime"}, + Name{Name: "TableRetainStep", What: "the C retention runtime"}, + Name{Name: "TableRetainWalk", What: "the C retention runtime"}, + Name{Name: "table_retain_at", What: "the C retention runtime"}, + Name{Name: "table_retain_begin", What: "the C retention runtime"}, + Name{Name: "table_retain_capture", What: "the C retention runtime"}, + Name{Name: "table_retain_clear_placed", What: "the C retention runtime"}, + Name{Name: "table_retain_commit", What: "the C retention runtime"}, + Name{Name: "table_retain_count_lost", What: "the C retention runtime"}, + Name{Name: "table_retain_discard", What: "the C retention runtime"}, + Name{Name: "table_retain_finish", What: "the C retention runtime"}, + Name{Name: "table_retain_graph_save", What: "the C retention runtime"}, + Name{Name: "table_retain_here", What: "the C retention runtime"}, + Name{Name: "table_retain_id", What: "the C retention runtime"}, + Name{Name: "table_retain_in_content", What: "the C retention runtime"}, + Name{Name: "table_retain_in_frame", What: "the C retention runtime"}, + Name{Name: "table_retain_in_payload", What: "the C retention runtime"}, + Name{Name: "table_retain_in_ref", What: "the C retention runtime"}, + Name{Name: "table_retain_index", What: "the C retention runtime"}, + Name{Name: "table_retain_leb", What: "the C retention runtime"}, + Name{Name: "table_retain_leb_bytes", What: "the C retention runtime"}, + Name{Name: "table_retain_message_capture", What: "the C retention runtime"}, + Name{Name: "table_retain_message_content", What: "the C retention runtime"}, + Name{Name: "table_retain_message_frame", What: "the C retention runtime"}, + Name{Name: "table_retain_message_opaque", What: "the C retention runtime"}, + Name{Name: "table_retain_message_payload", What: "the C retention runtime"}, + Name{Name: "table_retain_message_scalar", What: "the C retention runtime"}, + Name{Name: "table_retain_nameable", What: "the C retention runtime"}, + Name{Name: "table_retain_out_content", What: "the C retention runtime"}, + Name{Name: "table_retain_out_frame", What: "the C retention runtime"}, + Name{Name: "table_retain_out_leb", What: "the C retention runtime"}, + Name{Name: "table_retain_out_leb_read", What: "the C retention runtime"}, + Name{Name: "table_retain_out_payload", What: "the C retention runtime"}, + Name{Name: "table_retain_out_raw", What: "the C retention runtime"}, + Name{Name: "table_retain_out_ref", What: "the C retention runtime"}, + Name{Name: "table_retain_payload", What: "the C retention runtime"}, + Name{Name: "table_retain_probe", What: "the C retention runtime"}, + Name{Name: "table_retain_put32", What: "the C retention runtime"}, + Name{Name: "table_retain_put64", What: "the C retention runtime"}, + Name{Name: "table_retain_raw", What: "the C retention runtime"}, + Name{Name: "table_retain_record_measure", What: "the C retention runtime"}, + Name{Name: "table_retain_records", What: "the C retention runtime"}, + Name{Name: "table_retain_ref", What: "the C retention runtime"}, + Name{Name: "table_retain_reserved", What: "the C retention runtime"}, + Name{Name: "table_retain_reset", What: "the C retention runtime"}, + Name{Name: "table_retain_rewind", What: "the C retention runtime"}, + Name{Name: "table_retain_root", What: "the C retention runtime"}, + Name{Name: "table_retain_step", What: "the C retention runtime"}, + Name{Name: "table_retain_tail", What: "the C retention runtime"}, + Name{Name: "table_retain_truncate", What: "the C retention runtime"}, + Name{Name: "table_retain_u32", What: "the C retention runtime"}, + Name{Name: "table_retain_u64", What: "the C retention runtime"}, + Name{Name: "table_retain_under", What: "the C retention runtime"}, + Name{Name: "table_retain_width", What: "the C retention runtime"}, + Name{Name: "table_retain_writer_id", What: "the C retention runtime"}, + + Name{Name: "TableBitReader", What: "the C message and announcement runtime"}, + Name{Name: "TableBitWriter", What: "the C message and announcement runtime"}, + Name{Name: "TableMessageEntry", What: "the C message and announcement runtime"}, + Name{Name: "TableMessageReader", What: "the C message and announcement runtime"}, + Name{Name: "TableMessageShapeFacts", What: "the C message and announcement runtime"}, + Name{Name: "TableVocabulary", What: "the C message and announcement runtime"}, + Name{Name: "announce", What: "the C message and announcement runtime"}, + Name{Name: "announce_measure", What: "the C message and announcement runtime"}, + Name{Name: "announce_read", What: "the C message and announcement runtime"}, + Name{Name: "kTableAnnounce", What: "the C message and announcement runtime"}, + Name{Name: "kTableAnnounceBytes", What: "the C message and announcement runtime"}, + Name{Name: "kTableMessageBatchMax", What: "the C message and announcement runtime"}, + Name{Name: "kTableMessageEntriesHere", What: "the C message and announcement runtime"}, + Name{Name: "kTableMessageRefBitsHere", What: "the C message and announcement runtime"}, + Name{Name: "table_announce_once", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_align_read", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_align_write", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_get", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_getbytes", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_has", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_put", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_putbytes", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_reader", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_skip", What: "the C message and announcement runtime"}, + Name{Name: "table_bit_writer", What: "the C message and announcement runtime"}, + Name{Name: "table_bits_required", What: "the C message and announcement runtime"}, + Name{Name: "table_message_blob_scan", What: "the C message and announcement runtime"}, + Name{Name: "table_message_close", What: "the C message and announcement runtime"}, + Name{Name: "table_message_count", What: "the C message and announcement runtime"}, + Name{Name: "table_message_dequantize", What: "the C message and announcement runtime"}, + Name{Name: "table_message_element", What: "the C message and announcement runtime"}, + Name{Name: "table_message_entry_read", What: "the C message and announcement runtime"}, + Name{Name: "table_message_fixed_kind", What: "the C message and announcement runtime"}, + Name{Name: "table_message_float", What: "the C message and announcement runtime"}, + Name{Name: "table_message_integer_kind", What: "the C message and announcement runtime"}, + Name{Name: "table_message_kind_bits", What: "the C message and announcement runtime"}, + Name{Name: "table_message_known_kind", What: "the C message and announcement runtime"}, + Name{Name: "table_message_leb", What: "the C message and announcement runtime"}, + Name{Name: "table_message_nodes_open", What: "the C message and announcement runtime"}, + Name{Name: "table_message_number", What: "the C message and announcement runtime"}, + Name{Name: "table_message_open", What: "the C message and announcement runtime"}, + Name{Name: "table_message_quantization", What: "the C message and announcement runtime"}, + Name{Name: "table_message_quantize", What: "the C message and announcement runtime"}, + Name{Name: "table_message_ref", What: "the C message and announcement runtime"}, + Name{Name: "table_message_shape_read", What: "the C message and announcement runtime"}, + Name{Name: "table_message_skip", What: "the C message and announcement runtime"}, + Name{Name: "table_message_skip_body", What: "the C message and announcement runtime"}, + Name{Name: "table_message_skip_run", What: "the C message and announcement runtime"}, + Name{Name: "table_message_value_bits", What: "the C message and announcement runtime"}, + Name{Name: "table_vocabulary", What: "the C message and announcement runtime"}, + + Name{Name: "TableMapKey", What: "the C list, map and node-extent runtime"}, + Name{Name: "TableSequence", What: "the C list, map and node-extent runtime"}, + Name{Name: "TableSequenceCompare", What: "the C list, map and node-extent runtime"}, + Name{Name: "TableSequenceCursor", What: "the C list, map and node-extent runtime"}, + Name{Name: "TableSequenceFill", What: "the C list, map and node-extent runtime"}, + Name{Name: "TableSequenceHead", What: "the C list, map and node-extent runtime"}, + Name{Name: "TableSequenceOrder", What: "the C list, map and node-extent runtime"}, + Name{Name: "TableSequenceSegment", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_extent_reserve", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_json_interpret_exact", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_json_map_key_value", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_json_read_list", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_json_read_map", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_json_write_list", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_json_write_map", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_map_hash", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_map_index_slots", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_map_key", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_map_key_compare", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_add", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_cursor", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_erase", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_fill", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_fill_drop", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_fill_end", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_fill_next", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_next", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_order", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_orders_release", What: "the C list, map and node-extent runtime"}, + Name{Name: "table_sequence_sift", What: "the C list, map and node-extent runtime"}, + + Name{Name: "TableAllocator", What: "the C graph, blob and text runtime"}, + Name{Name: "TableBlob", What: "the C graph, blob and text runtime"}, + Name{Name: "TableBytesView", What: "the C graph, blob and text runtime"}, + Name{Name: "TableJsonGraphEntry", What: "the C graph, blob and text runtime"}, + Name{Name: "TableJsonGraphIn", What: "the C graph, blob and text runtime"}, + Name{Name: "TableJsonGraphMap", What: "the C graph, blob and text runtime"}, + Name{Name: "TableJsonGraphOut", What: "the C graph, blob and text runtime"}, + Name{Name: "TableNodeDirEntry", What: "the C graph, blob and text runtime"}, + Name{Name: "TableNodeEntry", What: "the C graph, blob and text runtime"}, + Name{Name: "TableNodeMap", What: "the C graph, blob and text runtime"}, + Name{Name: "TableNodeScan", What: "the C graph, blob and text runtime"}, + Name{Name: "TableNodeType", What: "the C graph, blob and text runtime"}, + Name{Name: "TableNodeWork", What: "the C graph, blob and text runtime"}, + Name{Name: "TableNumbering", What: "the C graph, blob and text runtime"}, + Name{Name: "TableStringView", What: "the C graph, blob and text runtime"}, + Name{Name: "kTableBytesTypeId", What: "the C graph, blob and text runtime"}, + Name{Name: "kTableStringTypeId", What: "the C graph, blob and text runtime"}, + Name{Name: "table_allocate", What: "the C graph, blob and text runtime"}, + Name{Name: "table_arena_grab_span", What: "the C graph, blob and text runtime"}, + Name{Name: "table_arena_init_with_allocator", What: "the C graph, blob and text runtime"}, + Name{Name: "table_blob_at", What: "the C graph, blob and text runtime"}, + Name{Name: "table_blob_edges", What: "the C graph, blob and text runtime"}, + Name{Name: "table_blob_emplace", What: "the C graph, blob and text runtime"}, + Name{Name: "table_blob_save", What: "the C graph, blob and text runtime"}, + Name{Name: "table_blob_storage", What: "the C graph, blob and text runtime"}, + Name{Name: "table_bytes_at", What: "the C graph, blob and text runtime"}, + Name{Name: "table_bytes_emplace", What: "the C graph, blob and text runtime"}, + Name{Name: "table_bytes_node_type", What: "the C graph, blob and text runtime"}, + Name{Name: "table_default_allocate", What: "the C graph, blob and text runtime"}, + Name{Name: "table_default_allocator", What: "the C graph, blob and text runtime"}, + Name{Name: "table_default_release", What: "the C graph, blob and text runtime"}, + Name{Name: "table_graph_pack", What: "the C graph, blob and text runtime"}, + Name{Name: "table_graph_records", What: "the C graph, blob and text runtime"}, + Name{Name: "table_graph_save", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_emplace", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_graph_find", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_graph_reach", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_graph_slot", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_read_blob", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_read_graph", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_read_pointer", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_read_table_keys", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_scan_label", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_skipped_ampersand", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_write_fields", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_write_graph", What: "the C graph, blob and text runtime"}, + Name{Name: "table_json_write_pointer", What: "the C graph, blob and text runtime"}, + Name{Name: "table_node_resolve", What: "the C graph, blob and text runtime"}, + Name{Name: "table_node_scan_begin", What: "the C graph, blob and text runtime"}, + Name{Name: "table_node_scan_next", What: "the C graph, blob and text runtime"}, + Name{Name: "table_node_scan_open", What: "the C graph, blob and text runtime"}, + Name{Name: "table_node_scan_whole", What: "the C graph, blob and text runtime"}, + Name{Name: "table_node_storage", What: "the C graph, blob and text runtime"}, + Name{Name: "table_node_wire_storage", What: "the C graph, blob and text runtime"}, + Name{Name: "table_number_build", What: "the C graph, blob and text runtime"}, + Name{Name: "table_number_dispose", What: "the C graph, blob and text runtime"}, + Name{Name: "table_number_edge", What: "the C graph, blob and text runtime"}, + Name{Name: "table_number_find", What: "the C graph, blob and text runtime"}, + Name{Name: "table_number_grow", What: "the C graph, blob and text runtime"}, + Name{Name: "table_number_hash", What: "the C graph, blob and text runtime"}, + Name{Name: "table_number_rehash", What: "the C graph, blob and text runtime"}, + Name{Name: "table_number_slot", What: "the C graph, blob and text runtime"}, + Name{Name: "table_ref_at", What: "the C graph, blob and text runtime"}, + Name{Name: "table_release", What: "the C graph, blob and text runtime"}, + Name{Name: "table_string_at", What: "the C graph, blob and text runtime"}, + Name{Name: "table_string_emplace", What: "the C graph, blob and text runtime"}, + Name{Name: "table_string_node_type", What: "the C graph, blob and text runtime"}, + Name{Name: "table_worker_alloc", What: "the C graph, blob and text runtime"}, + Name{Name: "schema_allocate", What: "the default zeroed allocation hook"}, + Name{Name: "schema_release", What: "the default release hook"}, + + Name{Name: "TableJsonInteger", What: "the text form integer conversion"}, + Name{Name: "table_json_interpret", What: "the text form integer conversion"}, + Name{Name: "table_json_integer_of", What: "the text form integer conversion"}, + Name{Name: "table_json_integer_in_domain", What: "the text form integer conversion"}, + Name{Name: "TableJsonWide", What: "the C table wide numeric and text runtime"}, + Name{Name: "TableWideRange", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_decode_utf8", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_kind_fixed", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_kind_wide", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_kind_wide_signed", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_read_wide", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_scan_unit", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_scan_w_string", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_compare", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_div", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_load", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_mul_add", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_neg", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_negative", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_shl", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_shr", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_store", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_wide_zero", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_write_w_string", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_json_write_wide", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_wire_utf16", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_wire_utf16_clamp", What: "the C table wide numeric and text runtime"}, + Name{Name: "table_wire_utf16_unit", What: "the C table wide numeric and text runtime"}, + + Name{Name: "table_reader_array_header", What: "the form-1 array header reader"}, + Name{Name: "table_wire_widen_f32", What: "exact f32 widening including NaN payloads"}, + Name{Name: "table_kind_widens", What: "the form-1 file wire runtime"}, + Name{Name: "table_reader_child", What: "the form-1 file wire runtime"}, + Name{Name: "table_reader_id_at", What: "the form-1 file wire runtime"}, + Name{Name: "table_reader_leb", What: "the form-1 file wire runtime"}, + Name{Name: "table_reader_room", What: "the form-1 file wire runtime"}, + Name{Name: "table_reader_span", What: "the form-1 file wire runtime"}, + Name{Name: "table_wire_open", What: "the form-1 file wire runtime"}, + Name{Name: "table_wire_utf8", What: "the form-1 file wire runtime"}, + Name{Name: "table_wire_utf8_clamp", What: "the form-1 file wire runtime"}, + Name{Name: "table_writer_finish", What: "the form-1 file wire runtime"}, + Name{Name: "table_writer_id", What: "the form-1 file wire runtime"}, + Name{Name: "table_writer_leb", What: "the form-1 file wire runtime"}, + Name{Name: "table_writer_probe", What: "the form-1 file wire runtime"}, Name{Name: "TableReport", What: "the read report — the permissive contract's ledger"}, + Name{Name: "TableWriteIds", What: "the file writer identity table"}, + Name{Name: "table_writer_rewind", What: "the file writer probe checkpoint"}, Name{Name: "TableWriter", What: "the wire writer over the caller's buffer"}, Name{Name: "TableReader", What: "the wire reader over the caller's buffer"}, Name{Name: "TableTypeInfo", What: "a table's reflection descriptor"}, @@ -64,7 +327,6 @@ func init() { Name{Name: "table_writer_put16", What: "the writer's little-endian u16"}, Name{Name: "table_writer_put32", What: "the writer's little-endian u32"}, Name{Name: "table_writer_put64", What: "the writer's little-endian u64"}, - Name{Name: "table_writer_patch32", What: "the writer's back-patch of a length prefix"}, Name{Name: "table_reader_make", What: "a reader over a caller's buffer"}, Name{Name: "table_reader_has", What: "the reader's remaining-bytes question"}, Name{Name: "table_reader_get8", What: "the reader's u8"}, @@ -80,7 +342,7 @@ func init() { // the ENUM-KEYED array (docs/SPEC-TABLES.md §2.4). C's storage IS the array, // so there is no TableKeyed to emit; what C++ puts in operator[] — the // left shift and the None refusal — lives in these two. - Name{Name: "table_keyed_slot", What: "the storage index a key names, refusing None in every build"}, + Name{Name: "table_keyed_slot", What: "the storage index a key names, refusing out-of-range keys in every build"}, // the VARIABLE-LENGTH runtime's C spellings (docs/SPEC-TABLES.md §6). The // arena and the worker are C++'s too; everything a member function or a // template did there is a name here. @@ -196,6 +458,25 @@ func init() { Name{Name: "table_block_default_allocator", What: "the malloc/free pair, for a caller with none of its own"}, Name{Name: "table_block_magic", What: "the block prologue's magic, and the byte-order check with it"}, Name{Name: "table_block_byte_order", What: "this build's byte order, as the prologue carries it"}, + Name{Name: "TableByteOrder", What: "the C canonical cook writer"}, + Name{Name: "TableByteOrder_Little", What: "the C canonical cook writer"}, + Name{Name: "TableByteOrder_Big", What: "the C canonical cook writer"}, + Name{Name: "table_cook_put", What: "the C canonical cook writer"}, + Name{Name: "table_cook_put128", What: "the C canonical cook writer"}, + Name{Name: "table_cook_bytes", What: "the C canonical cook writer"}, + Name{Name: "table_cook_units", What: "the C canonical cook writer"}, + Name{Name: "table_cook_header", What: "the C canonical cook writer"}, + Name{Name: "table_cook_ref", What: "the C canonical cook writer"}, + Name{Name: "table_cook_layout", What: "the C canonical cook writer"}, + Name{Name: "table_graph_cook", What: "the C canonical cook writer"}, + Name{Name: "TableRefuseReason", What: "the C cook and block refusal surface"}, + Name{Name: "table_cook_refuse", What: "the C cook and block refusal surface"}, + Name{Name: "table_cook_open_ex", What: "the C cook and block refusal surface"}, + Name{Name: "TableBlockConstRows", What: "the C cook and block refusal surface"}, + Name{Name: "table_block_row_at_const", What: "the C cook and block refusal surface"}, + Name{Name: "table_block_read32", What: "the C cook and block refusal surface"}, + Name{Name: "schema_assert", What: "the C keyed accessor assertion hook"}, + Name{Name: "schema_fatal", What: "the C keyed accessor fatal hook"}, Name{Name: "table_cook_open", What: "the cooked header's WHOLE check, shared by every Open"}, Name{Name: "table_cook_magic", What: "the cooked header's magic, and the byte-order check with it"}, Name{Name: "table_cook_byte_order", What: "this build's byte order, as a cooked header records it"}, diff --git a/internal/tablenames/cpp.go b/internal/tablenames/cpp.go index 23ab18eb2..ba8fb14f3 100644 --- a/internal/tablenames/cpp.go +++ b/internal/tablenames/cpp.go @@ -57,6 +57,7 @@ func init() { // reason docs/SPEC-TABLES.md:560 gives for an every-unit claim is // this one, and it reaches exactly this far. Name{Name: "TableTypeInfo", What: "a table's reflection descriptor", View: true}, + Name{Name: "TableWideRange", What: "a wide scalar reflection range", View: true}, Name{Name: "TableFieldInfo", What: "a field's reflection descriptor", View: true}, // THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1, §8.7): every field row // and every declaration row with no `///` block names this ONE diff --git a/internal/tablewire/bits.go b/internal/tablewire/bits.go index c6eb96034..a9983d860 100644 --- a/internal/tablewire/bits.go +++ b/internal/tablewire/bits.go @@ -7,7 +7,7 @@ package tablewire import "math/big" -// bitWriter accumulates the batch's bits. Nested bodies reach the wire in place: +// bitWriter accumulates the batch's bits. Nested bodies are written in place: // alignment is relative to the batch's byte zero (docs/SPEC-TABLES.md §3.3). type bitWriter struct { b []byte diff --git a/internal/tablewire/decode.go b/internal/tablewire/decode.go index 1ec5e937e..12b9744f6 100644 --- a/internal/tablewire/decode.go +++ b/internal/tablewire/decode.go @@ -1474,8 +1474,9 @@ func (r *wireReader) scalarAt(cell *tabletext.Cell, f *ir.Field, kind int) bool } cell.I = value cell.U = uint64(value) - // The read already zero-extended raw. A receiver range can clamp above - // the source width; masking now would truncate that valid widened value. + // raw was already zero-extended when read. A declared range may clamp a + // widened value above the source width; masking here would truncate the + // result back to the old width (u8 255 -> u64 min 1000 became 232). return true } diff --git a/internal/tablewire/widen_test.go b/internal/tablewire/widen_test.go new file mode 100644 index 000000000..41f8e4a9d --- /dev/null +++ b/internal/tablewire/widen_test.go @@ -0,0 +1,50 @@ +package tablewire_test + +import ( + "encoding/binary" + "testing" + + "github.com/mas-bandwidth/schema/v2/internal/check" + "github.com/mas-bandwidth/schema/v2/internal/parser" + "github.com/mas-bandwidth/schema/v2/internal/tabletext" + "github.com/mas-bandwidth/schema/v2/internal/tablewire" + "github.com/mas-bandwidth/schema/v2/ir" +) + +// The range belongs to the reader's declaration, not the incoming width. +// Re-masking after the clamp used to truncate this value back to u8/u16/u32. +func TestUnsignedWideningClampsAtDeclaredWidth(t *testing.T) { + const source = `package widen + table Root { value uint64 = 1099511627776 | min = 1099511627776, max = 2199023255552 + } + ` + ast, parseErrs := parser.Parse("Wide.schema", []byte(source)) + if len(parseErrs) != 0 { + t.Fatal(parseErrs) + } + unit, checkErrs := check.Unit([]check.SourceFile{{Path: "Wide.schema", Name: "Wide.schema", Base: "Wide", Bytes: []byte(source), AST: ast}}) + if len(checkErrs) != 0 { + t.Fatal(checkErrs) + } + model := tabletext.NewModel(unit) + for _, kind := range []int{ir.TableKindU8, ir.TableKindU16, ir.TableKindU32} { + value := model.New(unit.Tables["Root"]) + wire := []byte{1, 1, byte(kind)} + for i := 0; i < ir.TableKindWidth(kind); i++ { + wire = append(wire, 255) + } + wire = append(wire, 0) + wire = binary.LittleEndian.AppendUint64(wire, ir.TableFieldWireId(value.Fields[0].Def)) + wire = binary.LittleEndian.AppendUint64(wire, 1) + var report tabletext.Report + if ok, err := tablewire.Decode(model, value, wire, &report); !ok || err != nil { + t.Fatalf("kind %d: decode %v, %v", kind, ok, err) + } + if got := value.Fields[0].Cell.U; got != 1<<40 { + t.Fatalf("kind %d: clamped to %d, want %d", kind, got, uint64(1)<<40) + } + if report.Widened != 1 || report.Clamped != 1 || report.Malformed { + t.Fatalf("kind %d: report %+v", kind, report) + } + } +} diff --git a/make/c.mk b/make/c.mk index efa24a4f0..30512e843 100644 --- a/make/c.mk +++ b/make/c.mk @@ -90,8 +90,26 @@ build/schema_test_c_ludicrous: generated/c-ludicrous/.stamp test/c-ludicrous/mai # together — the generated externals carry the package (internal/codegen/ctable's # `sym`) — but they cannot be INCLUDED into one translation unit, which is what # the conformance driver's file-per-unit shape is about. -build/tables-generated-c/.stamp: bin/schema $(SCHEMAS_TABLES) $(SCHEMAS_TABLES_POINTERS) $(SCHEMAS_TABLES_BLOCK) test/tables/V1.schema test/tables/V2.schema test/tables/P1.schema test/tables/P2.schema test/tables/P3.schema test/tables/JsonKeys.schema +build/tables-generated-c/.stamp: bin/schema build/tables-generated-c/collections.stamp make/c.mk test/tables/W1.schema test/tables/W2.schema test/tables/G1.schema $(wildcard tables/stream/*.schema) $(wildcard tables/blobs/*.schema) $(wildcard tables/vocab9/*.schema) $(wildcard tables/vocab/*.schema) $(wildcard tables/backend/*.schema) test/tables/R2.schema test/tables/R1.schema test/tables/K2.schema test/tables/K1.schema test/tables/A2.schema test/tables/A1.schema test/tables/M2.schema test/tables/M1.schema $(wildcard tables/messages/*.schema) $(SCHEMAS_TABLES) $(SCHEMAS_TABLES_POINTERS) $(SCHEMAS_TABLES_BLOCK) test/tables/V1.schema test/tables/V2.schema test/tables/P1.schema test/tables/P2.schema test/tables/P3.schema test/tables/JsonKeys.schema tables/scalars/Scalars.schema test/tables/Scalars2.schema examples-wide/Caption.schema examples-wide/WideText.schema @mkdir -p build/tables-generated-c + ./bin/schema generate --lang c --out build/tables-generated-c/w1 test/tables/W1.schema + ./bin/schema generate --lang c --out build/tables-generated-c/w2 test/tables/W2.schema + ./bin/schema generate --lang c --out build/tables-generated-c/g1 test/tables/G1.schema + ./bin/schema generate --lang c --out build/tables-generated-c/stream tables/stream + ./bin/schema generate --lang c --out build/tables-generated-c/blobs tables/blobs + ./bin/schema generate --lang c --out build/tables-generated-c/vocab9 tables/vocab9 + ./bin/schema generate --lang c --out build/tables-generated-c/vocab tables/vocab + ./bin/schema generate --lang c --out build/tables-generated-c/backend tables/backend + ./bin/schema generate --lang c --out build/tables-generated-c/r2 test/tables/R2.schema + ./bin/schema generate --lang c --out build/tables-generated-c/r1 test/tables/R1.schema + ./bin/schema generate --lang c --out build/tables-generated-c/rt1 test/tables/RT1.schema + ./bin/schema generate --lang c --out build/tables-generated-c/k2 test/tables/K2.schema + ./bin/schema generate --lang c --out build/tables-generated-c/k1 test/tables/K1.schema + ./bin/schema generate --lang c --out build/tables-generated-c/a2 test/tables/A2.schema + ./bin/schema generate --lang c --out build/tables-generated-c/a1 test/tables/A1.schema + ./bin/schema generate --lang c --out build/tables-generated-c/m2 test/tables/M2.schema + ./bin/schema generate --lang c --out build/tables-generated-c/m1 test/tables/M1.schema + ./bin/schema generate --lang c --out build/tables-generated-c/messages tables/messages ./bin/schema generate --lang c --out build/tables-generated-c/examples tables/examples ./bin/schema generate --lang c --out build/tables-generated-c/pointers tables/pointers ./bin/schema generate --lang c --out build/tables-generated-c/block tables/block @@ -101,6 +119,9 @@ build/tables-generated-c/.stamp: bin/schema $(SCHEMAS_TABLES) $(SCHEMAS_TABLES_P ./bin/schema generate --lang c --out build/tables-generated-c/p1 test/tables/P1.schema ./bin/schema generate --lang c --out build/tables-generated-c/p2 test/tables/P2.schema ./bin/schema generate --lang c --out build/tables-generated-c/p3 test/tables/P3.schema + ./bin/schema generate --lang c --out build/tables-generated-c/wide examples-wide + ./bin/schema generate --lang c --out build/tables-generated-c/scalars tables/scalars + ./bin/schema generate --lang c --out build/tables-generated-c/scalars2 test/tables/Scalars2.schema ./bin/schema generate --lang c --out build/tables-generated-c/jsonkeys test/tables/JsonKeys.schema @touch $@ @@ -117,7 +138,30 @@ TABLES_CFLAGS := -std=c99 -Wall -Wextra -Werror -Wshadow -Wtype-limits $(C_TAUTO # these. TABLES_CFLAGS_CONTROL := $(subst -O2,-O0,$(TABLES_CFLAGS)) -C_CONFORMANCE_SOURCES = test/conformance/c/main.c \ +C_CONFORMANCE_SOURCES = test/c-tables/collections_fuzz_maps.c test/c-tables/collections_fuzz_lists.c test/c-tables/collections_fuzz_arms.c \ + $(patsubst tables/%.schema,build/tables-generated-c/%Table.c,$(SCHEMAS_TABLES_MAPS) $(SCHEMAS_TABLES_LISTS) $(SCHEMAS_TABLES_ARMS)) \ + test/conformance/c/unit_tblw1.c build/tables-generated-c/w1/W1Table.c \ + test/conformance/c/unit_tblw2.c build/tables-generated-c/w2/W2Table.c \ + test/conformance/c/main.c test/conformance/c/retain.c build/tables-generated-c/rt1/RT1Table.c \ + test/conformance/c/unit_tblg1.c build/tables-generated-c/g1/G1Table.c \ + test/conformance/c/unit_tblp2.c build/tables-generated-c/p2/P2Table.c \ + test/conformance/c/unit_streamdemo.c build/tables-generated-c/stream/StreamTable.c \ + test/conformance/c/unit_blobdemo.c build/tables-generated-c/blobs/AssetsTable.c \ + test/conformance/c/unit_vocab9demo.c build/tables-generated-c/vocab9/Vocab9Table.c \ + test/conformance/c/unit_vocabdemo.c build/tables-generated-c/vocab/VocabTable.c \ + test/conformance/c/unit_backenddemo.c build/tables-generated-c/backend/BackendTable.c \ + test/conformance/c/unit_tblr2.c build/tables-generated-c/r2/R2Table.c \ + test/conformance/c/unit_tblr1.c build/tables-generated-c/r1/R1Table.c \ + test/conformance/c/unit_tblk2.c build/tables-generated-c/k2/K2Table.c \ + test/conformance/c/unit_tblk1.c build/tables-generated-c/k1/K1Table.c \ + test/conformance/c/unit_tbla2.c build/tables-generated-c/a2/A2Table.c \ + test/conformance/c/unit_tbla1.c build/tables-generated-c/a1/A1Table.c \ + test/conformance/c/unit_tblm2.c build/tables-generated-c/m2/M2Table.c \ + test/conformance/c/unit_tblm1.c build/tables-generated-c/m1/M1Table.c \ + test/conformance/c/unit_messagedemo.c build/tables-generated-c/messages/MessagesTable.c \ + test/conformance/c/unit_widedemo.c build/tables-generated-c/wide/CaptionTable.c \ + test/conformance/c/unit_scalars.c test/conformance/c/unit_tblscalars2.c \ + build/tables-generated-c/scalars/ScalarsTable.c build/tables-generated-c/scalars2/Scalars2Table.c \ test/conformance/c/unit_tabledemo.c test/conformance/c/unit_tblv1.c \ test/conformance/c/unit_tblv2.c test/conformance/c/unit_tblp1.c \ test/conformance/c/unit_tblp3.c test/conformance/c/unit_blockdemo.c \ @@ -136,7 +180,7 @@ C_CONFORMANCE_SOURCES = test/conformance/c/main.c \ # Each unit's translation unit gets ONLY its own unit on the include path, which # is what keeps two units' identically-named headers from meeting. The driver's # own headers come from test/conformance/c. -C_CONFORMANCE_INCLUDES := -Itest/conformance/c -Ibuild/tables-generated-c/examples \ +C_CONFORMANCE_INCLUDES := -Ibuild/tables-generated-c -Ibuild/tables-generated-c/w1 -Ibuild/tables-generated-c/w2 -Ibuild/tables-generated-c/rt1 -Ibuild/tables-generated-c/g1 -Ibuild/tables-generated-c/p2 -Ibuild/tables-generated-c/stream -Ibuild/tables-generated-c/blobs -Ibuild/tables-generated-c/vocab9 -Ibuild/tables-generated-c/vocab -Ibuild/tables-generated-c/backend -Ibuild/tables-generated-c/r2 -Ibuild/tables-generated-c/r1 -Ibuild/tables-generated-c/k2 -Ibuild/tables-generated-c/k1 -Ibuild/tables-generated-c/a2 -Ibuild/tables-generated-c/a1 -Ibuild/tables-generated-c/m2 -Ibuild/tables-generated-c/m1 -Ibuild/tables-generated-c/messages -Ibuild/tables-generated-c/wide -I$(SERIALIZE_C) -Ibuild/tables-generated-c/scalars -Ibuild/tables-generated-c/scalars2 -Itest/conformance/c -Ibuild/tables-generated-c/examples \ -Ibuild/tables-generated-c/v1 -Ibuild/tables-generated-c/v2 \ -Ibuild/tables-generated-c/p1 -Ibuild/tables-generated-c/p3 \ -Ibuild/tables-generated-c/block -Ibuild/tables-generated-c/pointers @@ -155,7 +199,7 @@ tables-c-zero-cost: build/tables-generated-c/.stamp @for f in build/tables-generated-c/examples/*Table.h build/tables-generated-c/v1/*Table.h \ build/tables-generated-c/v2/*Table.h build/tables-generated-c/p1/*Table.h \ build/tables-generated-c/p3/*Table.h; do \ - if grep -nE "TableArena|TableWorker|TableRef|TableSink|TableCtx|TableRegionSink|kTableSegment|kTableSlab|kTableMaxDepth|is_pointer|Builder|PackMeasure|LoadMeasure|stdatomic" $$f; then \ + if grep -nE "TableArena|TableWorker|TableRef([^u]|$$)|TableSink|TableCtx|TableRegionSink|kTableSegment|kTableSlab|kTableMaxDepth|is_pointer|Builder|PackMeasure|LoadMeasure|stdatomic" $$f; then \ echo "ZERO-COST GATE FAILED: pointer machinery leaked into $$f"; exit 1; \ fi; \ done @@ -230,14 +274,10 @@ build/schema_test_c_soak: build/tables-generated-c/.stamp test/c-tables/soak_mai build/tables-generated-c/v1/V1Table.c build/tables-generated-c/v2/V2Table.c \ build/tables-generated-c/p1/P1Table.c build/tables-generated-c/p3/P3Table.c -o $@ -lm -# THE SOAK IS DORMANT while this port writes the wire's previous form (schema -# #512), on the same rule as the big-endian leg below: the soak refuses to run -# at all until the codec re-saves every case in testdata/wire/tables to its own -# bytes, and those bytes are the id-table form. The binary above is what the -# leg wakes with. What is absent is the corpus it holds itself to. +# The file-wire corpus and allocation counters run together. .PHONY: tables-c-soak -tables-c-soak: - @echo "tables-c-soak: dormant — the corpus it gates against is absent while this port writes the wire's previous form (docs/SPEC-TABLES.md §3, schema#512)" +tables-c-soak: build/schema_test_c_soak + ./build/schema_test_c_soak $(SOAK_SECONDS) .PHONY: tables-c-fuzz tables-c-fuzz: build/schema_test_c_fuzz @@ -246,7 +286,7 @@ tables-c-fuzz: build/schema_test_c_fuzz # THE C TABLES LEG, whole. Everything above, plus the conformance driver under # the sanitizers over every surface it answers. .PHONY: tables-c -tables-c: build/conformance-c build/conformance-c-asan tables-c-zero-cost tables-c-json-walk tables-c-fuzz tables-c-fuzz-negative-control tables-c-variable +tables-c: tables-c-wire-fuzz build/conformance-c build/conformance-c-asan tables-c-zero-cost tables-c-json-walk tables-c-fuzz tables-c-fuzz-negative-control tables-c-variable ./build/conformance-harness run --drivers test/conformance/c/drivers-asan.txt --work build/conformance-c-asan-work $(MAKE) tables-js-leg $(MAKE) tables-js-accessor-negative-control @@ -268,9 +308,74 @@ tables-c: build/conformance-c build/conformance-c-asan tables-c-zero-cost tables # Nothing tracked is written to: the emitter source is patched into a COPY and # reached through a Go build overlay, so an interrupt cannot leave a sabotaged # working tree. +CONFORMANCE_NEGATIVE_C := build/conformance-negative-c +CONFORMANCE_NEGATIVE_C_SED := s|const TableFieldInfo \* f = &info->fields\[index\];|const TableFieldInfo * f = \&info->fields[( index ^ 1 ) < info->num_fields ? ( index ^ 1 ) : index]; /* SABOTAGED */| .PHONY: conformance-negative-control-c -conformance-negative-control-c: - @echo "conformance-negative-control-c: dormant — the surface it turns red is absent while this port writes the wire's previous form (docs/SPEC-TABLES.md §3, schema#512)" +conformance-negative-control-c: build/conformance-harness + @rm -rf $(CONFORMANCE_NEGATIVE_C) && mkdir -p $(CONFORMANCE_NEGATIVE_C) + @sed '$(CONFORMANCE_NEGATIVE_C_SED)' internal/codegen/ctable/json.go > $(CONFORMANCE_NEGATIVE_C)/ctable-json.go.txt + @cmp -s internal/codegen/ctable/json.go $(CONFORMANCE_NEGATIVE_C)/ctable-json.go.txt && \ + { echo "NEGATIVE CONTROL: the C emitter sabotage did not apply"; exit 1; } || true + @printf '{"Replace":{"%s/internal/codegen/ctable/json.go":"%s/$(CONFORMANCE_NEGATIVE_C)/ctable-json.go.txt"}}\n' \ + "$(CURDIR)" "$(CURDIR)" > $(CONFORMANCE_NEGATIVE_C)/overlay.json + go build -overlay $(CONFORMANCE_NEGATIVE_C)/overlay.json -o $(CONFORMANCE_NEGATIVE_C)/schema ./cmd/schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/maps tables/maps + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/lists tables/lists + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/arms tables/arms + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/w1 test/tables/W1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/w2 test/tables/W2.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/stream tables/stream + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/g1 test/tables/G1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/blobs tables/blobs + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/vocab9 tables/vocab9 + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/vocab tables/vocab + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/backend tables/backend + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/r2 test/tables/R2.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/r1 test/tables/R1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/rt1 test/tables/RT1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/k2 test/tables/K2.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/k1 test/tables/K1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/a2 test/tables/A2.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/a1 test/tables/A1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/m2 test/tables/M2.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/m1 test/tables/M1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/messages tables/messages + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/examples tables/examples + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/pointers tables/pointers + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/block tables/block + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/blockhome tables/blockhome + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/v1 test/tables/V1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/v2 test/tables/V2.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/p1 test/tables/P1.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/p2 test/tables/P2.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/p3 test/tables/P3.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/wide examples-wide + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/scalars tables/scalars + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/scalars2 test/tables/Scalars2.schema + $(CONFORMANCE_NEGATIVE_C)/schema generate --lang c --out $(CONFORMANCE_NEGATIVE_C)/generated/jsonkeys test/tables/JsonKeys.schema + @grep -lq SABOTAGED $(CONFORMANCE_NEGATIVE_C)/generated/*/*Table.c || \ + { echo "NEGATIVE CONTROL FAILED: the sabotaged emitter emitted an unsabotaged walk"; exit 1; } + $(CC) $(TABLES_CFLAGS_CONTROL) $(subst build/tables-generated-c,$(CONFORMANCE_NEGATIVE_C)/generated,$(C_CONFORMANCE_INCLUDES)) \ + $(subst build/tables-generated-c,$(CONFORMANCE_NEGATIVE_C)/generated,$(C_CONFORMANCE_SOURCES)) -o $(CONFORMANCE_NEGATIVE_C)/driver-bin -lm + @printf '#!/bin/sh\nexec "%s/driver-bin" "$$@"\n' "$(CURDIR)/$(CONFORMANCE_NEGATIVE_C)" > $(CONFORMANCE_NEGATIVE_C)/driver + @chmod +x $(CONFORMANCE_NEGATIVE_C)/driver + @printf 'c %s/driver\n' "$(CONFORMANCE_NEGATIVE_C)" > $(CONFORMANCE_NEGATIVE_C)/drivers.txt + @if ./build/conformance-harness run --drivers $(CONFORMANCE_NEGATIVE_C)/drivers.txt \ + --work $(CONFORMANCE_NEGATIVE_C)/work > $(CONFORMANCE_NEGATIVE_C)/log 2>&1; then \ + echo "NEGATIVE CONTROL FAILED: a sabotaged C walker left the harness green"; \ + cat $(CONFORMANCE_NEGATIVE_C)/log; exit 1; \ + fi + @grep -q "c / json-read" $(CONFORMANCE_NEGATIVE_C)/log || \ + { echo "NEGATIVE CONTROL FAILED: the harness went red, but not on the sabotaged surface"; \ + cat $(CONFORMANCE_NEGATIVE_C)/log; exit 1; } + @grep -q "json-write pass" $(CONFORMANCE_NEGATIVE_C)/log || \ + { echo "NEGATIVE CONTROL FAILED: json-write went red too, so the control does not localise the READER"; \ + cat $(CONFORMANCE_NEGATIVE_C)/log; exit 1; } + @grep -q "wire pass" $(CONFORMANCE_NEGATIVE_C)/log || \ + { echo "NEGATIVE CONTROL FAILED: the whole matrix went red, so it localises nothing"; \ + cat $(CONFORMANCE_NEGATIVE_C)/log; exit 1; } + @grep -m1 "c / json-read" $(CONFORMANCE_NEGATIVE_C)/log + @echo "negative control: one field index off in the C walk turns the harness RED on json-read alone" # THE NEGATIVE CONTROL FOR THE TWO FOREIGN SURFACES. `cook-foreign` and # `block-foreign` are the only rows whose EXPECTED ANSWER IS A REFUSAL, so a @@ -291,7 +396,7 @@ conformance-negative-control-c-foreign: build/conformance-harness build/tables-g $(CC) $(TABLES_CFLAGS_CONTROL) $(C_CONFORMANCE_INCLUDES) \ $(CONFORMANCE_NEGATIVE_C_FOREIGN)/main.c $(filter-out test/conformance/c/main.c,$(C_CONFORMANCE_SOURCES)) \ -o $(CONFORMANCE_NEGATIVE_C_FOREIGN)/driver-bin -lm - @printf '#!/bin/sh\nexec %s/driver-bin "$$@"\n' "$(CURDIR)/$(CONFORMANCE_NEGATIVE_C_FOREIGN)" > $(CONFORMANCE_NEGATIVE_C_FOREIGN)/driver + @printf '#!/bin/sh\nexec "%s/driver-bin" "$$@"\n' "$(CURDIR)/$(CONFORMANCE_NEGATIVE_C_FOREIGN)" > $(CONFORMANCE_NEGATIVE_C_FOREIGN)/driver @chmod +x $(CONFORMANCE_NEGATIVE_C_FOREIGN)/driver @printf 'c %s/driver\n' "$(CONFORMANCE_NEGATIVE_C_FOREIGN)" > $(CONFORMANCE_NEGATIVE_C_FOREIGN)/drivers.txt @if ./build/conformance-harness run --drivers $(CONFORMANCE_NEGATIVE_C_FOREIGN)/drivers.txt \ @@ -322,12 +427,6 @@ conformance-negative-control-c-foreign: build/conformance-harness build/tables-g # cross-compiled and run for zero seconds: it loads the whole corpus, re-saves # every exact case and byte-compares, then stops. # -# THE LEG IS DORMANT while this port writes the wire's previous form (schema -# #512): the goldens under testdata/wire/tables are the id-table form, and a -# codec that cannot reproduce them on a LITTLE-endian host cannot be asked what -# it does on a big-endian one. The binary below is what the leg wakes with, and -# it still cross-compiles; what is absent is the corpus it would gate against. -# # BE_CC names what CI installed, the way BE_CXX does for the C++ legs; the pair # is not a system binary and not assumed. BE_CC ?= s390x-linux-gnu-gcc @@ -347,8 +446,9 @@ build/schema_test_c_soak_be: build/tables-generated-c/.stamp test/c-tables/soak_ build/tables-generated-c/p1/P1Table.c build/tables-generated-c/p3/P3Table.c -o $@ -lm .PHONY: tables-c-big-endian -tables-c-big-endian: - @echo "tables-c-big-endian: dormant — the corpus it gates against is absent while this port writes the wire's previous form (docs/SPEC-TABLES.md §3, schema#512)" +tables-c-big-endian: build/schema_test_c_soak_be + $(BE_RUN) ./build/schema_test_c_soak_be 0 + @echo "big-endian C leg: the tolerant wire crosses the byte order — same goldens, byte for byte" # THE KEYED None REFUSAL, C side (docs/SPEC-TABLES.md §2.4). C's accessor is a # macro over table_keyed_slot rather than an operator[] — the one spelling that @@ -369,7 +469,7 @@ tables-c-keyed-none-refusal-ndebug: build/tables-generated-c/.stamp test/c-table .PHONY: tables-c-keyed-none-refusal-negative-control tables-c-keyed-none-refusal-negative-control: bin/schema test/c-tables/keyed_none_ndebug_main.c @rm -rf build/c-keyed-sabotage && mkdir -p build/c-keyed-sabotage - @sed 's| abort();| /* SABOTAGED: the abort is gone */ (void) 0;|' \ + @sed 's| schema_fatal();| /* SABOTAGED: the abort is gone */ (void) 0;|' \ internal/codegen/ctable/ctable.go > build/c-keyed-sabotage/ctable.go.txt @cmp -s internal/codegen/ctable/ctable.go build/c-keyed-sabotage/ctable.go.txt && \ { echo "NEGATIVE CONTROL: the sabotage patched nothing"; exit 1; } || true @@ -438,8 +538,8 @@ tables-c-variable: build/schema_test_c_variable build/schema_test_c_variable_asa tables-c-fuzz-negative-control: build/tables-generated-c/.stamp build/cook-open/.stamp @rm -rf build/c-fuzz-sabotage && mkdir -p build/c-fuzz-sabotage @cp -r build/tables-generated-c/block build/c-fuzz-sabotage/ - @sed -i.bak -e 's|if ( rows > (uint64_t) bytes - offset_of ) { return 0; }|/* SABOTAGED */|' \ - -e 's|if ( padding > bytes - used ) { return 0; }|/* SABOTAGED */|' \ + @sed -i.bak -e 's|if(rows>(uint64_t)bytes-offset) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_LAYOUT)!=NULL; }|/* SABOTAGED */|' \ + -e 's|if(padding>bytes-used) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED)!=NULL; }|/* SABOTAGED */|' \ build/c-fuzz-sabotage/block/RenderBlock.c @grep -q SABOTAGED build/c-fuzz-sabotage/block/RenderBlock.c || \ { echo "NEGATIVE CONTROL: the sabotage patched nothing"; exit 1; } @@ -471,8 +571,33 @@ tables-c-fuzz-negative-control: build/tables-generated-c/.stamp build/cook-open/ # The drift gate must stay silent — that is the half being demonstrated — and # the call count must refuse. .PHONY: tables-c-soak-negative-control -tables-c-soak-negative-control: - @echo "tables-c-soak-negative-control: dormant — the surface it turns red is absent while this port writes the wire's previous form (docs/SPEC-TABLES.md §3, schema#512)" +tables-c-soak-negative-control: build/tables-generated-c/.stamp + @rm -rf build/c-soak-sabotage && mkdir -p build/c-soak-sabotage + @sed 's| codec->load( value, loaded\[i\].wire, (int64_t) loaded\[i\].bytes, \&report );| { void * sabotage = malloc( 1 ); *(volatile char *) sabotage = 1; free( sabotage ); } /* SABOTAGED: one matched pair, invisible to a live-byte sample. The volatile store is what stops the optimiser deleting a dead allocation outright, which gcc does at -O2 — a control the compiler removed proves nothing. */\n codec->load( value, loaded[i].wire, (int64_t) loaded[i].bytes, \&report );|' \ + test/c-tables/soak_main.c > build/c-soak-sabotage/soak_main.c + @grep -q SABOTAGED build/c-soak-sabotage/soak_main.c || \ + { echo "NEGATIVE CONTROL: the sabotage patched nothing"; exit 1; } + $(CC) $(TABLES_CFLAGS) $(C_CONFORMANCE_INCLUDES) \ + build/c-soak-sabotage/soak_main.c test/conformance/c/unit_tabledemo.c test/conformance/c/unit_tblv1.c \ + test/conformance/c/unit_tblv2.c test/conformance/c/unit_tblp1.c test/conformance/c/unit_tblp3.c \ + build/tables-generated-c/examples/TablesTable.c build/tables-generated-c/examples/WideTable.c \ + build/tables-generated-c/examples/NestedTable.c build/tables-generated-c/examples/KeyedTable.c \ + build/tables-generated-c/examples/PackTable.c build/tables-generated-c/examples/GuardedTable.c \ + build/tables-generated-c/examples/RangesTable.c \ + build/tables-generated-c/v1/V1Table.c build/tables-generated-c/v2/V2Table.c \ + build/tables-generated-c/p1/P1Table.c build/tables-generated-c/p3/P3Table.c \ + -o build/c-soak-sabotage/soak -lm + @if ./build/c-soak-sabotage/soak 2 > build/c-soak-sabotage/log 2>&1; then \ + echo "NEGATIVE CONTROL FAILED: a malloc/free pair per iteration left the soak green"; \ + cat build/c-soak-sabotage/log; exit 1; \ + fi + @grep -q "allocator call" build/c-soak-sabotage/log || \ + { echo "NEGATIVE CONTROL FAILED: the soak went red, but not on the call count"; \ + cat build/c-soak-sabotage/log; exit 1; } + @grep -q "live allocation" build/c-soak-sabotage/log || \ + { echo "NEGATIVE CONTROL FAILED: the drift half did not run"; cat build/c-soak-sabotage/log; exit 1; } + @grep -m1 "SOAK FAILED" build/c-soak-sabotage/log + @echo "negative control: a matched malloc/free pair per iteration is INVISIBLE to the drift gate and turns the CALL COUNT red" # The C half of `make update-goldens`: the committed generated table sources # (testdata/golden/tables/*-c). @@ -480,8 +605,28 @@ tables-c-soak-negative-control: update-goldens-c: build/tables-generated-c/.stamp @for d in examples block pointers; do \ mkdir -p testdata/golden/tables/$$d-c; \ - cp build/tables-generated-c/$$d/*Table.h build/tables-generated-c/$$d/*Table.c testdata/golden/tables/$$d-c/ 2>/dev/null || true; \ + cp build/tables-generated-c/$$d/*Table.h build/tables-generated-c/$$d/*Table.c build/tables-generated-c/$$d/*View.h build/tables-generated-c/$$d/*View.c testdata/golden/tables/$$d-c/; \ + done + +# The same registry listing oracle as the reference, through the C surface. +.PHONY: tables-c-view +tables-c-view: bin/schema test/c-tables/view_main.c + @rm -rf build/c-view + @mkdir -p build/c-view + @set -e; for entry in $(VIEW_CORPUS); do \ + dir=$${entry%%:*}; pkg=$${entry##*:}; \ + cap=$$(printf '%s' "$$pkg" | cut -c1 | tr 'a-z' 'A-Z')$$(printf '%s' "$$pkg" | cut -c2-); \ + source=tables/$$dir; if [ "$$dir" = wide ]; then source=examples-wide; fi; \ + ./bin/schema generate --lang c --out build/c-view/$$dir $$source; \ + $(CC) $(TABLES_CFLAGS) -Ibuild/c-view/$$dir -I$(SERIALIZE_C) \ + -DVIEW_HEADER="\"$${cap}View.h\"" test/c-tables/view_main.c \ + build/c-view/$$dir/*Table.c build/c-view/$$dir/*View.c -o build/c-view/prog-$$pkg -lm; \ + ./build/c-view/prog-$$pkg > build/c-view/$$pkg.listing; \ done + SCHEMA_VIEW_LISTING_DIR="$(CURDIR)/build/c-view" go test ./internal/viewlisting -run TestUnitViewListingMatchesTheIR + @echo "C unit registry: $(words $(VIEW_CORPUS)) units match the independent listing" + +test-c tables-c: tables-c-view # THE C LEG of `make test` (docs/SPEC-TABLES.md; test/conformance/README.md): # the same corpus in C, with the two gates that hold the emitter honest, the @@ -497,6 +642,7 @@ update-goldens-c: build/tables-generated-c/.stamp # SOAK_SECONDS=3600`. .PHONY: test-c test-c: build/schema_test_c build/schema_test_c_ludicrous build/schema_test_bench_c build/conformance-harness build/conformance-c build/conformance-c-asan build/schema_test_c_fuzz build/schema_test_c_soak build/schema_test_c_variable build/schema_test_c_variable_asan + $(MAKE) tables-c-wire-fuzz SEED=1 N=20000 $(MAKE) tables-c-zero-cost $(MAKE) tables-c-json-walk $(MAKE) tables-c-fuzz N=25000 @@ -520,6 +666,20 @@ TEST_LEGS += test-c CONFORMANCE_LEGS += build/conformance-c BENCH_TABLES_LEGS += generated/bench/tables/c/.stamp GOLDENS_LEGS += update-goldens-c + +# The file-wire differential: the compiler's independent engine owns every +# expected byte and report. Unsupported roster entries are named absent. +build/wire-fuzz-c: build/tables-generated-c/.stamp test/c-tables/wire_fuzz_main.c $(wildcard test/conformance/c/*.h) $(wildcard test/conformance/c/*.c) + $(CC) $(TABLES_CFLAGS) $(C_CONFORMANCE_INCLUDES) test/c-tables/wire_fuzz_main.c $(filter-out test/conformance/c/main.c,$(C_CONFORMANCE_SOURCES)) -o $@ -lm + +build/wire-fuzz-c-asan: build/tables-generated-c/.stamp test/c-tables/wire_fuzz_main.c $(wildcard test/conformance/c/*.h) $(wildcard test/conformance/c/*.c) + $(CC) $(TABLES_CFLAGS_CONTROL) $(C_SANITIZE) $(C_CONFORMANCE_INCLUDES) test/c-tables/wire_fuzz_main.c $(filter-out test/conformance/c/main.c,$(C_CONFORMANCE_SOURCES)) -o $@ -lm + +.PHONY: tables-c-wire-fuzz +tables-c-wire-fuzz: build/conformance-harness build/wire-fuzz-c build/wire-fuzz-c-asan + ./build/conformance-harness wire-fuzz --driver ./build/wire-fuzz-c --seed $(SEED) --n $(N) + ./build/conformance-harness wire-fuzz --driver ./build/wire-fuzz-c-asan --seed $(SEED) --n $(N) --failed build/wire-fuzz/failed-c-asan.bin + # Wide text on the packet wire, using the shared group's corpus. build/packet-wide/c/.stamp: bin/schema build/packet-wide/source/WideText.schema ./bin/schema generate --lang c --out build/packet-wide/c build/packet-wide/source/WideText.schema @@ -552,3 +712,112 @@ packet-wide-c-negative-control: packet-wide-c @echo 'packet wide C negative control: removed pairing fails bit-flip agreement' test-c: packet-wide-c packet-wide-c-negative-control + +# Show that the independent differential detects a permissive LEB128 reader. +# Only generated scratch headers change; no working source is sabotaged. +.PHONY: tables-c-wire-fuzz-negative-control +tables-c-wire-fuzz-negative-control: build/conformance-harness build/tables-generated-c/.stamp + @rm -rf build/c-wire-sabotage && mkdir -p build/c-wire-sabotage + @cp -R build/tables-generated-c build/c-wire-sabotage/generated + @for f in build/c-wire-sabotage/generated/*/*Table.h; do \ + sed 's/if ( i \&\& b == 0 )/if ( 0 ) \/* SABOTAGED canonical LEB128 *\//' "$$f" > "$$f.tmp" && mv "$$f.tmp" "$$f" || exit 1; \ + done + @grep -q 'SABOTAGED canonical LEB128' build/c-wire-sabotage/generated/examples/TablesTable.h || { echo 'NEGATIVE CONTROL: canonical LEB128 sabotage did not apply'; exit 1; } + $(CC) $(TABLES_CFLAGS_CONTROL) $(subst build/tables-generated-c,build/c-wire-sabotage/generated,$(C_CONFORMANCE_INCLUDES)) \ + test/c-tables/wire_fuzz_main.c $(subst build/tables-generated-c,build/c-wire-sabotage/generated,$(filter-out test/conformance/c/main.c,$(C_CONFORMANCE_SOURCES))) -o build/c-wire-sabotage/driver -lm + @if ./build/conformance-harness wire-fuzz --driver ./build/c-wire-sabotage/driver --seed 1 --n 0 --failed build/c-wire-sabotage/failed.bin > build/c-wire-sabotage/log 2>&1; then \ + echo 'NEGATIVE CONTROL FAILED: noncanonical LEB128 was accepted and the differential stayed green'; exit 1; \ + fi + @grep -q 'the report differs' build/c-wire-sabotage/log || { cat build/c-wire-sabotage/log; exit 1; } + @echo 'C wire negative control: accepting noncanonical LEB128 changes the read report' + +test-c: tables-c-wire-fuzz-negative-control + +# Maps and lists use the C++ reference's pinned file bytes and exact region +# sizes. Both allocator-backed construction and caller-owned loads are checked +# through lock, save, and JSON under native execution and ASan/UBSan. +build/tables-generated-c/collections.stamp: bin/schema make/c.mk $(wildcard tables/maps/*.schema) $(wildcard tables/lists/*.schema) $(wildcard tables/arms/*.schema) + ./bin/schema generate --lang c --out build/tables-generated-c/maps tables/maps + ./bin/schema generate --lang c --out build/tables-generated-c/lists tables/lists + ./bin/schema generate --lang c --out build/tables-generated-c/arms tables/arms + @touch $@ + +build/c-collections-maps: build/tables-generated-c/collections.stamp test/c-tables/collections_maps.c test/c-tables/collections.h + $(CC) $(TABLES_CFLAGS) -Ibuild/tables-generated-c/maps test/c-tables/collections_maps.c build/tables-generated-c/maps/*Table.c -o $@ -lm + +build/c-collections-lists: build/tables-generated-c/collections.stamp test/c-tables/collections_lists.c test/c-tables/collections.h + $(CC) $(TABLES_CFLAGS) -Ibuild/tables-generated-c/lists test/c-tables/collections_lists.c build/tables-generated-c/lists/*Table.c -o $@ -lm + +build/c-collections-maps-asan: build/tables-generated-c/collections.stamp test/c-tables/collections_maps.c test/c-tables/collections.h + $(CC) $(TABLES_CFLAGS_CONTROL) $(C_SANITIZE) -Ibuild/tables-generated-c/maps test/c-tables/collections_maps.c build/tables-generated-c/maps/*Table.c -o $@ -lm + +build/c-collections-lists-asan: build/tables-generated-c/collections.stamp test/c-tables/collections_lists.c test/c-tables/collections.h + $(CC) $(TABLES_CFLAGS_CONTROL) $(C_SANITIZE) -Ibuild/tables-generated-c/lists test/c-tables/collections_lists.c build/tables-generated-c/lists/*Table.c -o $@ -lm + +.PHONY: tables-c-collections +tables-c-collections: build/c-collections-maps build/c-collections-lists build/c-collections-maps-asan build/c-collections-lists-asan + ./build/c-collections-maps + ./build/c-collections-lists + ./build/c-collections-maps-asan + ./build/c-collections-lists-asan + +test-c tables-c: tables-c-collections + +build/collections-cpp/.stamp: bin/schema make/c.mk $(wildcard tables/maps/*.schema) $(wildcard tables/lists/*.schema) $(wildcard tables/arms/*.schema) + ./bin/schema generate --lang cpp --out build/collections-cpp/maps tables/maps + ./bin/schema generate --lang cpp --out build/collections-cpp/lists tables/lists + ./bin/schema generate --lang cpp --out build/collections-cpp/arms tables/arms + @touch $@ + +build/c-collections-fuzz: build/tables-generated-c/collections.stamp test/c-tables/collections_fuzz_maps.c test/c-tables/collections_fuzz_lists.c test/c-tables/collections_fuzz_arms.c test/c-tables/wire_fuzz_main.c $(wildcard test/conformance/c/*.h) + $(CC) $(TABLES_CFLAGS) -DSCHEMA_C_COLLECTIONS_FUZZ -Itest/conformance/c -Ibuild/tables-generated-c test/c-tables/wire_fuzz_main.c test/c-tables/collections_fuzz_maps.c test/c-tables/collections_fuzz_lists.c test/c-tables/collections_fuzz_arms.c build/tables-generated-c/maps/*Table.c build/tables-generated-c/lists/*Table.c build/tables-generated-c/arms/*Table.c -o $@ -lm + +build/c-collections-fuzz-asan: build/tables-generated-c/collections.stamp test/c-tables/collections_fuzz_maps.c test/c-tables/collections_fuzz_lists.c test/c-tables/collections_fuzz_arms.c test/c-tables/wire_fuzz_main.c $(wildcard test/conformance/c/*.h) + $(CC) $(TABLES_CFLAGS_CONTROL) $(C_SANITIZE) -DSCHEMA_C_COLLECTIONS_FUZZ -Itest/conformance/c -Ibuild/tables-generated-c test/c-tables/wire_fuzz_main.c test/c-tables/collections_fuzz_maps.c test/c-tables/collections_fuzz_lists.c test/c-tables/collections_fuzz_arms.c build/tables-generated-c/maps/*Table.c build/tables-generated-c/lists/*Table.c build/tables-generated-c/arms/*Table.c -o $@ -lm + +build/cpp-collections-fuzz: build/collections-cpp/.stamp test/c-tables/collections_fuzz.cpp test/c-tables/wire_fuzz_main.c test/conformance/c/driver.h + $(CXX) -std=c++17 -Wall -Wextra -Werror -O2 -Itest/conformance/c -Ibuild/collections-cpp test/c-tables/collections_fuzz.cpp -o $@ + +.PHONY: tables-c-collections-fuzz +tables-c-collections-fuzz: build/c-collections-fuzz build/c-collections-fuzz-asan build/cpp-collections-fuzz + SCHEMA_C_COLLECTIONS_DRIVER=./build/c-collections-fuzz SCHEMA_CPP_COLLECTIONS_DRIVER=./build/cpp-collections-fuzz go test ./test/conformance/harness -run '^TestCCollection(Cook)?Differential$$' -count=1 -v + SCHEMA_C_COLLECTIONS_DRIVER=./build/c-collections-fuzz-asan SCHEMA_CPP_COLLECTIONS_DRIVER=./build/cpp-collections-fuzz go test ./test/conformance/harness -run '^TestCCollection(Cook)?Differential$$' -count=1 -v + +test-c tables-c: tables-c-collections-fuzz + +# A wrong compile-time field slot must turn the independently encoded mixed +# message batch red. The generated writer is sabotaged only through an overlay. +.PHONY: tables-c-message-negative-control +tables-c-message-negative-control: + @mkdir -p build/c-message-negative + go run ./tools/sabotage -name message-c-wrong-slot -out build/c-message-negative/message_save.gotext internal/codegen/ctable/message_save.go + @printf '{"Replace":{"%s/internal/codegen/ctable/message_save.go":"%s/build/c-message-negative/message_save.gotext"}}\n' "$(CURDIR)" "$(CURDIR)" > build/c-message-negative/overlay.json + @if go test -count=1 -overlay=build/c-message-negative/overlay.json ./compiler -run '^TestCTableMessageSave$$' > build/c-message-negative/log 2>&1; then \ + echo 'NEGATIVE CONTROL FAILED: the message slot changed without failing the wire comparison'; exit 1; \ + fi + @grep -q -- '--- FAIL: TestCTableMessageSave' build/c-message-negative/log + @grep -q 'memcmp(output,expected,sizeof(expected))' build/c-message-negative/log + @echo 'negative control: the wrong C message slot turns the independent wire comparison red' + +test-c tables-c: tables-c-message-negative-control + +.PHONY: tables-c-retain tables-c-retain-negative-control +tables-c-retain: build/conformance-harness build/wire-fuzz-c build/wire-fuzz-c-asan + go test ./compiler -run '^TestCTableRetain' -count=1 + ./build/conformance-harness wire-fuzz --driver ./build/wire-fuzz-c --retain --seed $(SEED) --n $(N) --failed build/wire-fuzz/failed-c-retain.bin + ./build/conformance-harness wire-fuzz --driver ./build/wire-fuzz-c-asan --retain --seed $(SEED) --n $(N) --failed build/wire-fuzz/failed-c-retain-asan.bin + +tables-c-retain-negative-control: + @mkdir -p build/c-retain-negative + go run ./tools/sabotage -name retain-c-drop-field -out build/c-retain-negative/retain.gotext internal/codegen/ctable/retain.go + @printf '{"Replace":{"%s/internal/codegen/ctable/retain.go":"%s/build/c-retain-negative/retain.gotext"}}\n' "$(CURDIR)" "$(CURDIR)" > build/c-retain-negative/overlay.json + @if go test -count=1 -overlay=build/c-retain-negative/overlay.json ./compiler -run '^TestCTableRetainFile$$' > build/c-retain-negative/log 2>&1; then \ + echo 'NEGATIVE CONTROL FAILED: silently dropped C retained field passed'; exit 1; fi + @grep -q -- '--- FAIL: TestCTableRetainFile' build/c-retain-negative/log + @grep -q -- 'report.retained==13' build/c-retain-negative/log + @echo 'C retention negative control: dropped field fails the public round-trip report' + +test-c tables-c: tables-c-retain tables-c-retain-negative-control + +# Collection adapters belong to the common roster as well as their direct differential. +build/conformance-c build/conformance-c-asan build/wire-fuzz-c build/wire-fuzz-c-asan: test/c-tables/collections_fuzz_maps.c test/c-tables/collections_fuzz_lists.c test/c-tables/collections_fuzz_arms.c diff --git a/make/negative-controls.json b/make/negative-controls.json index c1cdef584..f518ca9f4 100644 --- a/make/negative-controls.json +++ b/make/negative-controls.json @@ -63,18 +63,42 @@ { "name": "base-conformance", "when": "pull-request", - "why": "the conformance harness's own controls. The first of them rebuilds the C++ reference driver under a sabotage, which is 45 s of this group on the runner and the reason the absence control sits in a group of its own. Measured 22 s over 7 controls.", + "why": "The shared conformance harness controls, including the sabotaged C++ reference. The completed C driver now has three separate compile-heavy groups below; combining them exceeded the five-minute job ceiling on run 34186742566.", "targets": [ "conformance-negative-control", "conformance-negative-control-block-dump", - "conformance-negative-control-c", - "conformance-negative-control-c-foreign", "conformance-negative-control-go", "conformance-negative-control-go-walk", "tables-go-wire-fuzz-negative-control", "conformance-negative-control-reference-surface" ] }, + { + "name": "base-c-conformance", + "when": "pull-request", + "why": "The C JSON driver sabotage and the small message/retention compiler probes share one job. The expanded C driver must not share a job with the other two complete C driver rebuilds.", + "targets": [ + "conformance-negative-control-c", + "tables-c-message-negative-control", + "tables-c-retain-negative-control" + ] + }, + { + "name": "base-c-wire", + "when": "pull-request", + "why": "The complete C wire roster rebuilt with a permissive LEB reader took about 99 seconds on run 34186742566. Keep that compile in its own job within the control-time budget.", + "targets": [ + "tables-c-wire-fuzz-negative-control" + ] + }, + { + "name": "base-c-foreign", + "when": "pull-request", + "why": "The C foreign-byte-order sabotage rebuilds the complete driver. It was cancelled while compiling after the preceding C controls exhausted the old combined job; isolate it without raising the timeout.", + "targets": [ + "conformance-negative-control-c-foreign" + ] + }, { "name": "base-conformance-absence", "when": "pull-request", diff --git a/test/c-tables/collections.h b/test/c-tables/collections.h new file mode 100644 index 000000000..8baf1fb77 --- /dev/null +++ b/test/c-tables/collections.h @@ -0,0 +1,25 @@ +/* C collection parity against the C++ file-wire goldens and LoadMeasure. */ +#include +#include +#include +#define CHECK(x) do { if(!(x)) { fprintf(stderr,"%s:%d: %s\n",name,__LINE__,#x); return 0; } } while(0) +#define COLLECTION_CASE(TYPE,FN,NAME,NEED) \ +static int check_##NAME(void) { \ + const char * name=#NAME; FILE * input; uint8_t wire[65536],saved[65536]; size_t bytes; int64_t needed,text_bytes; \ + uint8_t * region; char * text; const TYPE * root; TYPE##Builder builder; TableReport report={0}; \ + input=fopen("testdata/wire/tables/" #NAME ".bin","rb");CHECK(input!=NULL);bytes=fread(wire,1,sizeof(wire),input);fclose(input); \ + needed=FN##_load_measure(wire,(int64_t)bytes);CHECK(needed==NEED); \ + region=(uint8_t *)malloc((size_t)needed);CHECK(region!=NULL); \ + root=FN##_load(region,needed,wire,(int64_t)bytes,&report);CHECK(root!=NULL); \ + CHECK(!report.malformed && !report.unknown && !report.kind_mismatch && !report.clamped && !report.duplicate && !report.widened); \ + CHECK(FN##_save(NULL,root,saved,sizeof(saved))==(int64_t)bytes);CHECK(!memcmp(wire,saved,bytes)); \ + CHECK(FN##_builder_init(&builder));CHECK(FN##_load_builder(&builder,wire,(int64_t)bytes,&report));CHECK(FN##_builder_lock(&builder)); \ + CHECK(FN##_save(NULL,(const TYPE *)(const void *)builder.region,saved,sizeof(saved))==(int64_t)bytes);CHECK(!memcmp(wire,saved,bytes)); \ + FN##_builder_shutdown(&builder); \ + text_bytes=FN##_to_json_measure(root);CHECK(text_bytes>0 && text_bytes<1048576);text=(char *)malloc((size_t)text_bytes+1);CHECK(text!=NULL); \ + CHECK(FN##_to_json(root,text,text_bytes)==text_bytes); \ + CHECK(FN##_builder_init(&builder));CHECK(FN##_from_json(&builder,text,text_bytes,&report)); \ + CHECK(!report.malformed && !report.unknown && !report.kind_mismatch && !report.clamped && !report.duplicate);CHECK(FN##_builder_lock(&builder)); \ + CHECK(FN##_save(NULL,(const TYPE *)(const void *)builder.region,saved,sizeof(saved))==(int64_t)bytes);CHECK(!memcmp(wire,saved,bytes)); \ + FN##_builder_shutdown(&builder);free(text);free(region);return 1; \ +} diff --git a/test/c-tables/collections_fuzz.cpp b/test/c-tables/collections_fuzz.cpp new file mode 100644 index 000000000..f1e572477 --- /dev/null +++ b/test/c-tables/collections_fuzz.cpp @@ -0,0 +1,110 @@ +// The unchanged C++ reference codecs on the C driver's differential protocol. +#include "driver.h" +#include "maps/CellsTable.h" +#include "maps/ChunksTable.h" +#include "maps/CrewsTable.h" +#include "maps/DepthTable.h" +#include "maps/DocsTable.h" +#include "maps/FleetTable.h" +#include "maps/PairsTable.h" +#include "maps/RowsTable.h" +#include "maps/RunsTable.h" +#include "maps/SlotsTable.h" +#include "maps/SpansTable.h" +#include "maps/TextTable.h" +#include "maps/TrailsTable.h" +#include "lists/HoldersTable.h" +#include "lists/MigrateTable.h" +#include "lists/ReportTable.h" +#include "lists/SaveTable.h" +#include "lists/SharedTable.h" + +#include "arms/CarryTable.h" +#include "arms/GateTable.h" +#include "arms/NestTable.h" +#include "arms/RingTable.h" + +#define REFERENCE_CODEC(NS,TYPE) \ +static const NS::TYPE * reference_##NS##_##TYPE; \ +static void * reference_storage_##NS##_##TYPE() { free((void *)reference_##NS##_##TYPE); reference_##NS##_##TYPE=NULL; return &reference_##NS##_##TYPE; } \ +static int reference_load_##NS##_##TYPE(void * value,const uint8_t * wire,int64_t bytes,ConformanceReport * out) { \ + NS::TableReport report; int64_t need=NS::TYPE##LoadMeasure(wire,bytes); \ + uint8_t * region=(uint8_t *)malloc(need>0 ? (size_t)need : 1); \ + const NS::TYPE * root=NS::TYPE##Load(region,need,wire,bytes,&report); \ + *(const NS::TYPE **)value=root; if(root==NULL) { free(region); } \ + out->unknown=report.unknown;out->kind_mismatch=report.kind_mismatch;out->widened=report.widened;out->clamped=report.clamped; \ + out->duplicate=report.duplicate;out->malformed=report.malformed;out->refused=report.refused;return root!=NULL; } \ +static int64_t reference_measure_##NS##_##TYPE(const void * value) { const NS::TYPE * root=*(const NS::TYPE * const *)value;return root ? NS::TYPE##Measure(root) : -1; } \ +static int64_t reference_save_##NS##_##TYPE(const void * value,uint8_t * buffer,int64_t capacity) { return NS::TYPE##Save(*(const NS::TYPE * const *)value,buffer,capacity); } \ +static int64_t reference_load_measure_##NS##_##TYPE(const uint8_t * wire,int64_t bytes) { return NS::TYPE##LoadMeasure(wire,bytes); } \ +static int64_t reference_cook_measure_##NS##_##TYPE(const void * value) { return NS::TYPE##CookMeasure(*(const NS::TYPE * const *)value); } \ +static int reference_cook_##NS##_##TYPE(const void * value,void * buffer,uint64_t capacity,int big) { return NS::TYPE##Cook(*(const NS::TYPE * const *)value,buffer,capacity,big ? NS::TableByteOrder::Big : NS::TableByteOrder::Little); } +#define REFERENCE_ROW(NS,TYPE) {#NS,#TYPE,NULL,NULL,reference_load_##NS##_##TYPE,reference_measure_##NS##_##TYPE,reference_save_##NS##_##TYPE,NULL,NULL,reference_storage_##NS##_##TYPE,reference_load_measure_##NS##_##TYPE,reference_cook_measure_##NS##_##TYPE,reference_cook_##NS##_##TYPE,NULL} +REFERENCE_CODEC(mapdemo,Depth) +REFERENCE_CODEC(mapdemo,Text) +REFERENCE_CODEC(mapdemo,Cells) +REFERENCE_CODEC(mapdemo,Runs) +REFERENCE_CODEC(mapdemo,Slots) +REFERENCE_CODEC(mapdemo,Spans) +REFERENCE_CODEC(mapdemo,Docs) +REFERENCE_CODEC(mapdemo,Chunks) +REFERENCE_CODEC(mapdemo,Pairs) +REFERENCE_CODEC(mapdemo,Crews) +REFERENCE_CODEC(mapdemo,Trails) +REFERENCE_CODEC(mapdemo,Fleet) +const ConformanceCodec * conformance_codecs_mapdemo(int * count) { + static const ConformanceCodec codecs[]={ + REFERENCE_ROW(mapdemo,Depth), + REFERENCE_ROW(mapdemo,Text), + REFERENCE_ROW(mapdemo,Cells), + REFERENCE_ROW(mapdemo,Runs), + REFERENCE_ROW(mapdemo,Slots), + REFERENCE_ROW(mapdemo,Spans), + REFERENCE_ROW(mapdemo,Docs), + REFERENCE_ROW(mapdemo,Chunks), + REFERENCE_ROW(mapdemo,Pairs), + REFERENCE_ROW(mapdemo,Crews), + REFERENCE_ROW(mapdemo,Trails), + REFERENCE_ROW(mapdemo,Fleet), + }; *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} +REFERENCE_CODEC(listdemo,Save) +REFERENCE_CODEC(listdemo,Mixed) +REFERENCE_CODEC(listdemo,Album) +REFERENCE_CODEC(listdemo,Sheet) +REFERENCE_CODEC(listdemo,Army) +REFERENCE_CODEC(listdemo,Unbounded) +const ConformanceCodec * conformance_codecs_listdemo(int * count) { + static const ConformanceCodec codecs[]={ + REFERENCE_ROW(listdemo,Save), + REFERENCE_ROW(listdemo,Mixed), + REFERENCE_ROW(listdemo,Album), + REFERENCE_ROW(listdemo,Sheet), + REFERENCE_ROW(listdemo,Army), + REFERENCE_ROW(listdemo,Unbounded), + }; *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} + +REFERENCE_CODEC(armdemo,Holder) +REFERENCE_CODEC(armdemo,Hand) +REFERENCE_CODEC(armdemo,Chain) +REFERENCE_CODEC(armdemo,Gate) +REFERENCE_CODEC(armdemo,Nest) +REFERENCE_CODEC(armdemo,Ring) +REFERENCE_CODEC(armdemo,Rack) +REFERENCE_CODEC(armdemo,Tray) +const ConformanceCodec * conformance_codecs_armdemo(int * count) { + static const ConformanceCodec codecs[]={ + REFERENCE_ROW(armdemo,Holder), + REFERENCE_ROW(armdemo,Hand), + REFERENCE_ROW(armdemo,Chain), + REFERENCE_ROW(armdemo,Gate), + REFERENCE_ROW(armdemo,Nest), + REFERENCE_ROW(armdemo,Ring), + REFERENCE_ROW(armdemo,Rack), + REFERENCE_ROW(armdemo,Tray), + };*count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} + +#define SCHEMA_C_COLLECTIONS_FUZZ +#include "wire_fuzz_main.c" diff --git a/test/c-tables/collections_fuzz_arms.c b/test/c-tables/collections_fuzz_arms.c new file mode 100644 index 000000000..3ff9e4294 --- /dev/null +++ b/test/c-tables/collections_fuzz_arms.c @@ -0,0 +1,25 @@ +#include "arms/CarryTable.h" +#include "arms/GateTable.h" +#include "arms/NestTable.h" +#include "arms/RingTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Holder,holder) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Hand,hand) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Chain,chain) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Gate,gate) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Nest,nest) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Ring,ring) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Rack,rack) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Tray,tray) +const ConformanceCodec * conformance_codecs_armdemo(int * count) { + static const ConformanceCodec codecs[]={ + SCHEMA_CONFORMANCE_GRAPH_ROW(armdemo,Holder), + SCHEMA_CONFORMANCE_GRAPH_ROW(armdemo,Hand), + SCHEMA_CONFORMANCE_GRAPH_ROW(armdemo,Chain), + SCHEMA_CONFORMANCE_GRAPH_ROW(armdemo,Gate), + SCHEMA_CONFORMANCE_GRAPH_ROW(armdemo,Nest), + SCHEMA_CONFORMANCE_GRAPH_ROW(armdemo,Ring), + SCHEMA_CONFORMANCE_GRAPH_ROW(armdemo,Rack), + SCHEMA_CONFORMANCE_GRAPH_ROW(armdemo,Tray), + };*count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} diff --git a/test/c-tables/collections_fuzz_lists.c b/test/c-tables/collections_fuzz_lists.c new file mode 100644 index 000000000..71a788126 --- /dev/null +++ b/test/c-tables/collections_fuzz_lists.c @@ -0,0 +1,22 @@ +#include "lists/HoldersTable.h" +#include "lists/MigrateTable.h" +#include "lists/ReportTable.h" +#include "lists/SaveTable.h" +#include "lists/SharedTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Save,save) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Mixed,mixed) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Album,album) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Sheet,sheet) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Army,army) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Unbounded,unbounded) +const ConformanceCodec * conformance_codecs_listdemo(int * count) { + static const ConformanceCodec codecs[]={ + SCHEMA_CONFORMANCE_GRAPH_ROW(listdemo,Save), + SCHEMA_CONFORMANCE_GRAPH_ROW(listdemo,Mixed), + SCHEMA_CONFORMANCE_GRAPH_ROW(listdemo,Album), + SCHEMA_CONFORMANCE_GRAPH_ROW(listdemo,Sheet), + SCHEMA_CONFORMANCE_GRAPH_ROW(listdemo,Army), + SCHEMA_CONFORMANCE_GRAPH_ROW(listdemo,Unbounded), + }; *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} diff --git a/test/c-tables/collections_fuzz_maps.c b/test/c-tables/collections_fuzz_maps.c new file mode 100644 index 000000000..0a7155937 --- /dev/null +++ b/test/c-tables/collections_fuzz_maps.c @@ -0,0 +1,42 @@ +#include "maps/CellsTable.h" +#include "maps/ChunksTable.h" +#include "maps/CrewsTable.h" +#include "maps/DepthTable.h" +#include "maps/DocsTable.h" +#include "maps/FleetTable.h" +#include "maps/PairsTable.h" +#include "maps/RowsTable.h" +#include "maps/RunsTable.h" +#include "maps/SlotsTable.h" +#include "maps/SpansTable.h" +#include "maps/TextTable.h" +#include "maps/TrailsTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Depth,depth) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Text,text) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Cells,cells) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Runs,runs) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Slots,slots) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Spans,spans) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Docs,docs) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Chunks,chunks) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Pairs,pairs) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Crews,crews) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Trails,trails) +SCHEMA_CONFORMANCE_GRAPH_CODEC(Fleet,fleet) +const ConformanceCodec * conformance_codecs_mapdemo(int * count) { + static const ConformanceCodec codecs[]={ + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Depth), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Text), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Cells), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Runs), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Slots), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Spans), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Docs), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Chunks), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Pairs), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Crews), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Trails), + SCHEMA_CONFORMANCE_GRAPH_ROW(mapdemo,Fleet), + }; *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} diff --git a/test/c-tables/collections_lists.c b/test/c-tables/collections_lists.c new file mode 100644 index 000000000..483d9c8be --- /dev/null +++ b/test/c-tables/collections_lists.c @@ -0,0 +1,47 @@ +#include "collections.h" +#include "HoldersTable.h" +#include "MigrateTable.h" +#include "ReportTable.h" +#include "SaveTable.h" +#include "SharedTable.h" + +COLLECTION_CASE(Save,save,list_tables,168) +COLLECTION_CASE(Save,save,list_scalars,80) +COLLECTION_CASE(Save,save,list_empty,88) +COLLECTION_CASE(Save,save,list_erased,128) +COLLECTION_CASE(Mixed,mixed,list_mixed,152) +COLLECTION_CASE(Album,album,list_shared,88) +COLLECTION_CASE(Album,album,list_before_pointer,104) +COLLECTION_CASE(Sheet,sheet,list_nested,184) +COLLECTION_CASE(Army,army,list_of_maps,120) +COLLECTION_CASE(Unbounded,unbounded,list_migrates,56) + +static int check_large_list(void) { + const char * name="large_list"; BytesBuilder builder; Bytes * root; + int32_t i; const Bytes * locked; uint8_t * wire,*region; int64_t size,need; TableReport report={0}; + CHECK(bytes_builder_init(&builder));root=bytes_builder_root(&builder); + for(i=0;i<100000;i++) { uint8_t * slot=bytes_data_add(&builder.main,&root->data);CHECK(slot!=NULL);*slot=(uint8_t)i; } + CHECK(root->data.count==100000);CHECK(bytes_builder_lock(&builder));locked=(const Bytes *)(const void *)builder.region; + CHECK(*bytes_data_at(&locked->data,99999)==(uint8_t)99999);CHECK(bytes_data_at(&locked->data,100000)==NULL); + size=bytes_measure(NULL,locked);CHECK(size>100000);wire=(uint8_t *)malloc((size_t)size);CHECK(wire!=NULL); + CHECK(bytes_save(NULL,locked,wire,size)==size);need=bytes_load_measure(wire,size);CHECK(need>100000 && need<101000); + region=(uint8_t *)malloc((size_t)need);CHECK(region!=NULL);locked=bytes_load(region,need,wire,size,&report); + CHECK(locked!=NULL && !report.malformed && !report.clamped && locked->data.count==100000); + for(i=0;i<100000;i++) {CHECK(*bytes_data_at(&locked->data,i)==(uint8_t)i);} + free(region);free(wire);bytes_builder_shutdown(&builder);return 1; +} + +int main(void) { + if(!check_large_list()) { return 1; } + if(!check_list_tables()) { return 1; } + if(!check_list_scalars()) { return 1; } + if(!check_list_empty()) { return 1; } + if(!check_list_erased()) { return 1; } + if(!check_list_mixed()) { return 1; } + if(!check_list_shared()) { return 1; } + if(!check_list_before_pointer()) { return 1; } + if(!check_list_nested()) { return 1; } + if(!check_list_of_maps()) { return 1; } + if(!check_list_migrates()) { return 1; } + puts("C lists: wire, region sizes, builder, lock, and JSON match the C++ goldens"); return 0; +} diff --git a/test/c-tables/collections_maps.c b/test/c-tables/collections_maps.c new file mode 100644 index 000000000..24da5ae12 --- /dev/null +++ b/test/c-tables/collections_maps.c @@ -0,0 +1,63 @@ +#include "collections.h" +#include "CellsTable.h" +#include "ChunksTable.h" +#include "CrewsTable.h" +#include "DepthTable.h" +#include "DocsTable.h" +#include "FleetTable.h" +#include "PairsTable.h" +#include "RowsTable.h" +#include "RunsTable.h" +#include "SlotsTable.h" +#include "SpansTable.h" +#include "TextTable.h" +#include "TrailsTable.h" + +COLLECTION_CASE(Depth,depth,map_depth,192) +COLLECTION_CASE(Text,text,map_text,216) +COLLECTION_CASE(Cells,cells,map_cells,96) +COLLECTION_CASE(Runs,runs,map_runs,88) +COLLECTION_CASE(Slots,slots,map_slots,64) +COLLECTION_CASE(Spans,spans,map_spans,112) +COLLECTION_CASE(Docs,docs,map_docs,168) +COLLECTION_CASE(Chunks,chunks,map_chunks,136) +COLLECTION_CASE(Pairs,pairs,map_pairs,136) +COLLECTION_CASE(Crews,crews,map_crews,176) +COLLECTION_CASE(Trails,trails,map_trails,168) +COLLECTION_CASE(Fleet,fleet,map_full,640) +COLLECTION_CASE(Fleet,fleet,map_empty,88) + +static int check_segmented_map(void) { + const char * name="segmented_map"; WideRowBuilder builder; WideRow * root; const WideRow * locked; uint32_t i; + Item * first=NULL; TableCtx ctx; TableSequenceCursor cursor; const WideRowEntriesEntry * entry; + CHECK(wide_row_builder_init(&builder));root=wide_row_builder_root(&builder); + for(i=256;i>0;i--) { Item * slot=wide_row_entries_insert(&builder.main,&root->entries,i);CHECK(slot!=NULL);slot->count=(int32_t)i;if(i==256)first=slot; } + CHECK(first!=NULL && first->count==256);CHECK(wide_row_entries_find_mut(&builder.arena,&root->entries,256)==first); + CHECK(wide_row_entries_insert(&builder.main,&root->entries,256)==first);CHECK(first->count==0);first->count=256; + CHECK(wide_row_entries_erase(&builder.arena,&root->entries,33));CHECK(wide_row_entries_erase(&builder.arena,&root->entries,193)); + CHECK(root->entries.count==254);ctx.arena=&builder.arena;cursor=wide_row_entries_each(&ctx,&root->entries); + entry=(const WideRowEntriesEntry *)table_sequence_next(&cursor);CHECK(entry!=NULL && entry->key==256); + CHECK(wide_row_builder_lock(&builder));locked=(const WideRow *)(const void *)builder.region; + cursor=wide_row_entries_each(NULL,&locked->entries);i=0; + while((entry=(const WideRowEntriesEntry *)table_sequence_next(&cursor))!=NULL) { CHECK(entry->key>i && entry->value.count==(int32_t)entry->key);i=entry->key; } + CHECK(i==256 && wide_row_entries_find(&locked->entries,33)==NULL && wide_row_entries_find(&locked->entries,193)==NULL); + wide_row_builder_shutdown(&builder);return 1; +} + +int main(void) { + if(!check_segmented_map()) { return 1; } + if(!check_map_depth()) { return 1; } + if(!check_map_text()) { return 1; } + if(!check_map_cells()) { return 1; } + if(!check_map_runs()) { return 1; } + if(!check_map_slots()) { return 1; } + if(!check_map_spans()) { return 1; } + if(!check_map_docs()) { return 1; } + if(!check_map_chunks()) { return 1; } + if(!check_map_pairs()) { return 1; } + if(!check_map_crews()) { return 1; } + if(!check_map_trails()) { return 1; } + if(!check_map_full()) { return 1; } + if(!check_map_empty()) { return 1; } + puts("C maps: wire, region sizes, builder, lock, and JSON match the C++ goldens"); return 0; +} diff --git a/test/c-tables/keyed_none_ndebug_main.c b/test/c-tables/keyed_none_ndebug_main.c index e286ee763..a5cf587e7 100644 --- a/test/c-tables/keyed_none_ndebug_main.c +++ b/test/c-tables/keyed_none_ndebug_main.c @@ -40,7 +40,7 @@ int main( void ) Team key = TEAM_NONE; (void) quiet; keyed_config_reset( &cfg ); - SCHEMA_TABLE_KEYED_AT( cfg.teams, key ).spawn_count = 1; /* never reached */ + SCHEMA_TABLE_KEYED_AT( cfg.teams, key, TEAM_MAX ).spawn_count = 1; /* never reached */ _exit( 0 ); } if ( child < 0 ) diff --git a/test/c-tables/view_main.c b/test/c-tables/view_main.c new file mode 100644 index 000000000..ddb50d6aa --- /dev/null +++ b/test/c-tables/view_main.c @@ -0,0 +1,214 @@ +// THE EDITOR GATE, and it is a dogfood rather than a thought experiment +// (docs/SPEC-TABLES.md §8.7). This program has the generated view file and +// NOTHING ELSE: no schema files on disk, no compiler, no knowledge of a single +// declaration's name. It calls unit_view() and prints the whole build — every +// constant with its value, every enum and flags and union with its variants in +// order, every type and every table with every property — and the listing it +// prints is byte-compared against the listing the compiler produces from its +// own IR. If it cannot reach a declaration or a property of one, the view is +// incomplete and the gate says so. +// +// The unit is named by the build, not by this source: VIEW_HEADER is the +// generated header and VIEW_NAMESPACE the unit's package, so one program +// serves every unit in the corpus and knows nothing about any of them. +// +// A MULTI-LINE doc prints as ONE LINE, each newline written \n and the +// escape's own backslash written \\. The listing is a line-oriented byte +// comparison, so a column whose text carries newlines has to be flattened +// before it is compared, and both halves flattening it by the same rule is +// what keeps the comparison a comparison rather than a formatting argument. + +#include +#include + +#include VIEW_HEADER + +// THE PROGRAM CLAIMS NO NAME THE UNIT MIGHT DECLARE. A schema is free to +// declare `SchemaViewHolderProbe`, `round` or anything else, and this source is compiled +// against every unit in the corpus, so the package is reached through an alias +// rather than pulled in wholesale. + + +// flatten writes one doc column as a single line. It reads the column with NO +// NULL TEST: doc is never NULL (§8.1), and a NULL here faults rather than +// printing a line that happens to look right. +static void flatten( const char * doc ) +{ + for ( const char * p = doc; *p != 0; p++ ) + { +#ifdef VIEW_UNFLATTENED + // THE NEGATIVE CONTROL for the flattening rule: a printer that writes + // the newline verbatim splits a multi-line doc across lines, and the + // corpus gate's byte comparison must go red for it. + fputc( *p, stdout ); +#else + if ( *p == '\\' ) { fputs( "\\\\", stdout ); } + else if ( *p == '\n' ) { fputs( "\\n", stdout ); } + else { fputc( *p, stdout ); } +#endif + } +} + +// annotation prints a row's tags and its doc, the doc LAST because it is +// free-form text and everything after it on the line would be ambiguous. +static void annotation( int32_t num_tags, const char * const * tags, const char * doc ) +{ + fputs( " tags=[", stdout ); + for ( int32_t i = 0; i < num_tags; i++ ) + { + if ( i > 0 ) { fputc( ',', stdout ); } + fputs( tags[i], stdout ); + } + fputs( "] doc=", stdout ); + flatten( doc ); + fputc( '\n', stdout ); +} + +static void vocabulary( const char * what, const ViewVocabulary v, const char * row ) +{ + printf( "%s %s file=%s max=%lld bits=%d variants=%d", + what, v.name, v.file, (long long) v.max, v.storage_bits, v.num_variants ); + annotation( v.num_tags, v.tags, v.doc ); + for ( int32_t i = 0; i < v.num_variants; i++ ) + { + const ViewVariant r = v.variants[i]; + printf( "%s %s %s %llu %s id=%016llx", + what, v.name, row, (unsigned long long) r.value, r.name, (unsigned long long) r.id ); + annotation( r.num_tags, r.tags, r.doc ); + } +} + +// ---- the general arm's overlay (docs/SPEC-TABLES.md §8.7) ---- +// +// For each arm that names no record the listing carries the arm's kind and +// bounds off its FIELD descriptor, and where that arm's offset sits relative +// to the union's own payload base. A generic walker reaches those arm +// descriptors through a HOLDER, the first registry entry declaring a field of +// that union's type, taking the tables in registry order and then the types. +// It needs one because a union's arms table hangs off a field descriptor and +// not off the vocabulary row. +// +// EVERY ARM OVERLAYS, with the tag at offset 0 and the overlay after it +// (§8.1), so every arm's offset is the union's payload base and the OVERLAY +// column is 0 on every general arm. That is the column a moved arm offset goes +// red in, and it is the observable: the arm row's own offset, taken from the +// VIEW file, against the payload base the table header's arms table carries. +// The two are spelled in different translation units, so they can disagree. + +typedef struct SchemaViewHolderProbe +{ + const TableTypeInfo * type; + const TableFieldInfo * field; +} SchemaViewHolderProbe; + +static SchemaViewHolderProbe findSchemaViewHolderProbe( const UnitViewInfo * unit, const char * union_name ) +{ + SchemaViewHolderProbe none = { NULL, NULL }; + const ViewType * sets[2] = { unit->tables, unit->types }; + int32_t counts[2] = { unit->num_tables, unit->num_types }; + for ( int s = 0; s < 2; s++ ) + { + for ( int32_t i = 0; i < counts[s]; i++ ) + { + const TableTypeInfo * type = sets[s][i].type; + for ( int32_t f = 0; f < type->num_fields; f++ ) + { + if ( type->fields[f].arms != NULL && strcmp( type->fields[f].type_name, union_name ) == 0 ) + { + SchemaViewHolderProbe found = { type, &type->fields[f] }; + return found; + } + } + } + } + return none; +} + +static void unionVocabulary( const UnitViewInfo * unit, const ViewVocabulary v ) +{ + printf( "union %s file=%s max=%lld bits=%d variants=%d", + v.name, v.file, (long long) v.max, v.storage_bits, v.num_variants ); + annotation( v.num_tags, v.tags, v.doc ); + SchemaViewHolderProbe holder = findSchemaViewHolderProbe( unit, v.name ); + const TableUnionInfo * arms = holder.type != NULL ? holder.field->arms : NULL; + for ( int32_t i = 0; i < v.num_variants; i++ ) + { + const ViewVariant r = v.variants[i]; + printf( "union %s arm %llu %s id=%016llx payload=%s record=%s field=%s", + v.name, (unsigned long long) r.value, r.name, (unsigned long long) r.id, + r.payload_name == NULL ? "-" : r.payload_name, + r.payload != NULL ? "yes" : "no", + r.field != NULL ? "yes" : "no" ); + if ( r.field != NULL ) + { + printf( " kind=%d bound=%d", (int) r.field->kind, (int) r.field->array_bound ); + if ( arms != NULL ) + { + // EVERY ARM OVERLAYS THE UNION'S PAYLOAD BASE, so the distance + // from the first general arm's offset to this one's is zero + printf( " overlay=%d", (int) r.field->offset - (int) arms->arms[1].offset ); + } + else + { + printf( " overlay=-" ); + } + } + else + { + printf( " kind=- bound=- overlay=-" ); + } + annotation( r.num_tags, r.tags, r.doc ); + } +} + +static void declaration( const char * what, const ViewType entry ) +{ + const TableTypeInfo * type = entry.type; + printf( "%s %s file=%s fields=%d", what, entry.name, entry.file, type->num_fields ); + annotation( entry.num_tags, entry.tags, entry.doc ); + for ( int32_t i = 0; i < type->num_fields; i++ ) + { + const TableFieldInfo f = type->fields[i]; + printf( "%s %s field %s json=%s type=%s id=%016llx optional=%s", + what, entry.name, f.name, f.json == NULL ? "-" : f.json, f.type_name, + (unsigned long long) f.id, f.optional ? "true" : "false" ); + annotation( f.num_tags, f.tags, f.doc ); + } +} + +int main() +{ + const UnitViewInfo * unit = unit_view(); + printf( "unit package=%s protocol=%016llx\n", unit->package, (unsigned long long) unit->protocol_id ); + for ( int32_t i = 0; i < unit->num_constants; i++ ) + { + const ViewConstant c = unit->constants[i]; + uint64_t bits = 0; + memcpy( &bits, &c.float_value, sizeof( bits ) ); + printf( "constant %s file=%s type=%s float=%s int=%lld real=%016llx", + c.name, c.file, c.type_name, c.is_float ? "true" : "false", + (long long) c.int_value, (unsigned long long) ( c.is_float ? bits : 0 ) ); + annotation( c.num_tags, c.tags, c.doc ); + } + for ( int32_t i = 0; i < unit->num_enums; i++ ) + { + vocabulary( "enum", unit->enums[i], "variant" ); + } + for ( int32_t i = 0; i < unit->num_flags; i++ ) + { + vocabulary( "flags", unit->flags[i], "bit" ); + } + for ( int32_t i = 0; i < unit->num_unions; i++ ) + { + unionVocabulary( unit, unit->unions[i] ); + } + for ( int32_t i = 0; i < unit->num_types; i++ ) + { + declaration( "type", unit->types[i] ); + } + for ( int32_t i = 0; i < unit->num_tables; i++ ) + { + declaration( "table", unit->tables[i] ); + } + return 0; +} diff --git a/test/c-tables/wire_fuzz_main.c b/test/c-tables/wire_fuzz_main.c new file mode 100644 index 000000000..18c27c984 --- /dev/null +++ b/test/c-tables/wire_fuzz_main.c @@ -0,0 +1,104 @@ +/* The C leg of the independent table-wire differential. The protocol and + oracle live in test/conformance/harness; this process only loads and saves. */ +#include "driver.h" +#if defined(_WIN32) +#include +#include +#endif + +static int read_number( uint64_t * value, int bytes ) +{ + int i; *value = 0; + for ( i = 0; i < bytes; i++ ) { int b = getchar(); if ( b == EOF ) { return 0; } *value |= (uint64_t) b << (8*i); } + return 1; +} +static void write_number( uint64_t value, int bytes ) +{ + int i; for ( i = 0; i < bytes; i++ ) { putchar( (int) ((value >> (8*i)) & 255) ); } +} +static int read_name( char * name, size_t capacity ) +{ + uint64_t n; + if ( !read_number( &n, 2 ) || n >= capacity || fread( name, 1, (size_t) n, stdin ) != n ) { return 0; } + name[n] = 0; return 1; +} +typedef const ConformanceCodec * (*Unit)( int * count ); +static const ConformanceCodec * find_codec( const char * unit, const char * root ) +{ +#if defined(SCHEMA_C_COLLECTIONS_FUZZ) + const Unit units[] = { conformance_codecs_mapdemo, conformance_codecs_listdemo, conformance_codecs_armdemo }; +#else + const Unit units[] = { conformance_codecs_mapdemo, conformance_codecs_listdemo, conformance_codecs_armdemo, conformance_codecs_tblw1, conformance_codecs_tblw2, conformance_codecs_vocab9demo, conformance_codecs_vocabdemo, conformance_codecs_backenddemo, conformance_codecs_tblr2, conformance_codecs_tblr1, conformance_codecs_tblk2, conformance_codecs_tblk1, conformance_codecs_tbla2, conformance_codecs_tbla1, conformance_codecs_tblm2, conformance_codecs_tblm1, conformance_codecs_messagedemo, conformance_codecs_tabledemo, conformance_codecs_tblv1, + conformance_codecs_tblv2, conformance_codecs_tblp1, conformance_codecs_tblp3, conformance_codecs_widedemo, conformance_codecs_scalars, conformance_codecs_graphdemo, conformance_codecs_blobdemo, conformance_codecs_tblg1, conformance_codecs_tblp2, conformance_codecs_streamdemo, conformance_codecs_tblscalars2 }; +#endif + size_t u; + for ( u = 0; u < sizeof( units ) / sizeof( units[0] ); u++ ) { + int i, count; const ConformanceCodec * codecs = units[u]( &count ); + for ( i = 0; i < count; i++ ) { + if ( strcmp( codecs[i].unit, unit ) == 0 && strcmp( codecs[i].root, root ) == 0 ) { return &codecs[i]; } + } + } + return NULL; +} +int main( int argc, char ** argv ) +{ + uint64_t count, i, index, size; + int cook=argc>1 && strncmp(argv[1],"--cook-",7)==0; + int big=cook && strcmp(argv[1],"--cook-be")==0; + const ConformanceCodec ** roster; + uint8_t * forms; + #if defined(_WIN32) + _setmode( _fileno( stdin ), _O_BINARY ); + _setmode( _fileno( stdout ), _O_BINARY ); + #endif + if ( !read_number( &count, 4 ) || count > 4096 ) { return 1; } + roster = (const ConformanceCodec **) calloc( (size_t) count, sizeof( *roster ) ); + forms=(uint8_t *)calloc((size_t)count,1); + if ( roster == NULL || forms==NULL ) { return 1; } + for ( i = 0; i < count; i++ ) { + char unit[256], root[256]; uint64_t form, retain; + if ( !read_name( unit, sizeof( unit ) ) || !read_name( root, sizeof( root ) ) || + !read_number( &form, 1 ) || !read_number( &retain, 1 ) ) { return 1; } + forms[i]=(uint8_t)(retain?3:form); + if ( (form == 1 || (form==2&&!cook)) ) { roster[i] = find_codec( unit, root ); } + if(form==2 && roster[i] && !roster[i]->message_fuzz)roster[i]=NULL; + if(retain && roster[i] && !roster[i]->retain_fuzz)roster[i]=NULL; + putchar( roster[i] != NULL ); + } + fflush( stdout ); + while ( read_number( &index, 4 ) ) { + const ConformanceCodec * codec; + uint8_t * wire, * saved = NULL; void * value; int loaded; int64_t length, region_bytes; + ConformanceReport report; + if ( index >= count || roster[index] == NULL || !read_number( &size, 4 ) ) { return 1; } + codec = roster[index]; + wire = (uint8_t *) malloc( (size_t) (size ? size : 1) ); + if ( wire == NULL || fread( wire, 1, (size_t) size, stdin ) != size ) { return 1; } + memset( &report, 0, sizeof( report ) ); + if(forms[index]==3){length=codec->retain_fuzz(wire,(int64_t)size,&saved,&report,&loaded,®ion_bytes);free(wire);goto reply;} + if(forms[index]==2){length=codec->message_fuzz(wire,(int64_t)size,&saved,&report,&loaded,®ion_bytes);free(wire);goto reply;} + value = codec->storage(); + region_bytes=codec->load_measure ? codec->load_measure(wire,(int64_t)size) : -1; + loaded = codec->load( value, wire, (int64_t) size, &report ); free( wire ); + length = cook ? (loaded ? codec->cook_measure(value) : -1) : codec->measure( value ); + if(cook) { region_bytes=length; } + if ( length >= 0 ) { + saved = (uint8_t *) malloc( (size_t) (length ? length : 1) ); + if ( saved == NULL ) { return 1; } + if(cook) { if(!codec->cook(value,saved,(uint64_t)length,big)) { length=-1; } } + else { length = codec->save( value, saved, length ); } + } + /* Fixed storage always exists; load's bool is the body verdict. */ + if ( !loaded && !report.refused ) { report.malformed = 1; } + reply: + write_number( codec->load_measure ? (uint64_t)loaded : 1, 1 ); + write_number( (uint64_t) report.unknown, 4 ); write_number( (uint64_t) report.kind_mismatch, 4 ); + write_number( (uint64_t) report.widened, 4 ); write_number( (uint64_t) report.clamped, 4 ); + write_number( (uint64_t) report.duplicate, 4 ); write_number( (uint64_t) report.malformed, 1 ); + write_number( (uint64_t) report.refused, 1 ); write_number( (uint64_t)region_bytes, 8 ); + write_number( (uint64_t)report.retained, 4 ); write_number( (uint64_t)report.retain_lost, 4 ); write_number( (uint64_t) length, 8 ); + if ( length > 0 && fwrite( saved, 1, (size_t) length, stdout ) != (size_t) length ) { return 1; } + free( saved ); if ( fflush( stdout ) != 0 ) { return 1; } + } + free(forms); free( roster ); return ferror( stdin ) ? 1 : 0; +} diff --git a/test/conformance/README.md b/test/conformance/README.md index 4cdcc1b0d..610a522ca 100644 --- a/test/conformance/README.md +++ b/test/conformance/README.md @@ -121,29 +121,17 @@ they become part of the count as schema#349 and the per-construct follow-ons land them, one language at a time, with nothing in this data or this contract moving as they do. -**AND ONE ABSENCE IS WIDER THAN A CLASS: the WIRE FORM ITSELF.** The id-table -wire (docs/SPEC-TABLES.md §3) is the form byte, the reference-and-kind field -header, the canonical LEB128 numbers and the trailing id table. The C++ -reference and the tool write it. C# now carries form 1 for its supported fixed -roots: `make tables-cs-wire-fuzz` invokes its pipe driver and compares decoded -values, re-saved bytes and every report column with the independent engine. -Its five wire/text surfaces are active; unsupported roots and the message and -retention forms still answer absent. `conformance-negative-control-cs` removes -the duplicate-id check in an emitter overlay and requires the fuzzer to fail, -then shifts the text reader's field lookup and requires read failures while -wire, report and JSON-write still pass. - -The C# fixed roots include `tables/messages`, the M1/M2 arm-evolution pair, -and the A1/A2 arm-retype pair. These exercise table/general arms, union arrays -and optional arrays at several depths. This is the Messages corpus carried -in a form-1 file; form-2 message framing remains absent. - -The remaining seven ports still write the previous form. The harness prints -`N seeds absent (roots the leg has no codec for)`, applying the same -"absent is not failure" rule one grain up. The port work is schema#511 (go), -#512 (c), #513 (cs, remaining constructs), #514 (dart), #515 (elixir), #516 (js), -#517 (java) and #518 (rust), and PORTING.md -M20 is the register cell for each. +**Wire-form coverage is separate from storage coverage.** C++, C, C# and the +independent compiler engine carry the id-table file form and bitpacked message +form. C and C# also carry variable regions, builders, retained unknowns and +canonical cooks. Their pipe drivers compare values, re-saved bytes, measured +sizes and reports with the independent engine. `make tables-c-wire-fuzz` and +`make tables-c-retain` run C natively and under ASan/UBSan; the corresponding +C# managed, native, builder and retention targets are in make/cs.mk. + +Unsupported roots are printed as `N seeds absent (roots the leg has no codec +for)`. Absence is explicit and never counted as a passing fixture. +PORTING.md M20 records the coverage and gates. **Absent is not failure, and the distinction is the whole reason the matrix exists.** A backend with no text form is missing a FEATURE; a backend whose text diff --git a/test/conformance/c/ci.json b/test/conformance/c/ci.json index 37af0dffb..81df4e1fa 100644 --- a/test/conformance/c/ci.json +++ b/test/conformance/c/ci.json @@ -1 +1,2 @@ -{"targets": "build/conformance-harness build/conformance-c table-base64-c"} +{"targets": "build/conformance-harness build/conformance-c table-base64-c", + "runtime": "serialize.c", "runtime_tag": "SERIALIZE_C_TAG"} diff --git a/test/conformance/c/driver.h b/test/conformance/c/driver.h index db02a9fa7..65a8a521d 100644 --- a/test/conformance/c/driver.h +++ b/test/conformance/c/driver.h @@ -33,7 +33,7 @@ typedef struct ConformanceReport { int unknown, kind_mismatch, clamped, duplicate; - int malformed; + int malformed, refused, widened, retained, retain_lost; } ConformanceReport; /* One row per (unit, root) the corpus names. Every row is the SAME six @@ -43,12 +43,18 @@ typedef struct ConformanceCodec { const char * unit; const char * root; + int64_t (*message_fuzz)(const uint8_t * wire,int64_t bytes,uint8_t ** saved,ConformanceReport * report,int * loaded,int64_t * extent); + int (*message)(const uint8_t * announcement,int64_t announcement_bytes,const uint8_t * wire,int64_t bytes,uint8_t ** answer,int64_t * size,ConformanceReport * report); int ( *load )( void * value, const uint8_t * bytes, int64_t size, ConformanceReport * report ); int64_t ( *measure )( const void * value ); int64_t ( *save )( const void * value, uint8_t * buffer, int64_t capacity ); int ( *from_json )( void * value, const char * text, int64_t bytes, ConformanceReport * report ); int64_t ( *to_json )( const void * value, char * buffer, int64_t capacity ); void * ( *storage )( void ); /* one static instance per root, reset by load */ + int64_t (*load_measure)(const uint8_t * wire, int64_t bytes); + int64_t (*cook_measure)(const void * value); + int (*cook)(const void * value,void * buffer,uint64_t capacity,int big); + int64_t (*retain_fuzz)(const uint8_t *,int64_t,uint8_t **,ConformanceReport *,int *,int64_t *); } ConformanceCodec; /* A GROWING TEXT, for the two dumps. The harness compares bytes, so nothing @@ -80,12 +86,39 @@ SCHEMA_CONFORMANCE_UNUSED static void conformance_text_add( ConformanceText * ou conformance_text_raw( out, s, strlen( s ) ); } +int conformance_retain(int message,const uint8_t * announcement,int64_t announcement_bytes,const uint8_t * wire,int64_t bytes,int short_buffer,int64_t id_capacity,int * counters,uint8_t ** output,int64_t * output_bytes); + /* the per-unit entry points, each defined in that unit's own translation unit */ +const ConformanceCodec * conformance_codecs_armdemo(int * count); +const ConformanceCodec * conformance_codecs_mapdemo(int * count); +const ConformanceCodec * conformance_codecs_listdemo(int * count); +const ConformanceCodec * conformance_codecs_streamdemo(int * count); +const ConformanceCodec * conformance_codecs_tblw1(int * count); +const ConformanceCodec * conformance_codecs_tblw2(int * count); +const ConformanceCodec * conformance_codecs_tblp2(int * count); +const ConformanceCodec * conformance_codecs_tblg1(int * count); +const ConformanceCodec * conformance_codecs_blobdemo( int * count ); +const ConformanceCodec * conformance_codecs_messagedemo( int * count ); +const ConformanceCodec * conformance_codecs_tblm1( int * count ); +const ConformanceCodec * conformance_codecs_tblm2( int * count ); +const ConformanceCodec * conformance_codecs_tbla1( int * count ); +const ConformanceCodec * conformance_codecs_tbla2( int * count ); +const ConformanceCodec * conformance_codecs_tblk1( int * count ); +const ConformanceCodec * conformance_codecs_tblk2( int * count ); +const ConformanceCodec * conformance_codecs_tblr1( int * count ); +const ConformanceCodec * conformance_codecs_tblr2( int * count ); +const ConformanceCodec * conformance_codecs_backenddemo( int * count ); +const ConformanceCodec * conformance_codecs_vocabdemo( int * count ); +const ConformanceCodec * conformance_codecs_vocab9demo( int * count ); const ConformanceCodec * conformance_codecs_tabledemo( int * count ); +const ConformanceCodec * conformance_codecs_graphdemo( int * count ); const ConformanceCodec * conformance_codecs_tblv1( int * count ); const ConformanceCodec * conformance_codecs_tblv2( int * count ); const ConformanceCodec * conformance_codecs_tblp1( int * count ); const ConformanceCodec * conformance_codecs_tblp3( int * count ); +const ConformanceCodec * conformance_codecs_widedemo( int * count ); +const ConformanceCodec * conformance_codecs_scalars( int * count ); +const ConformanceCodec * conformance_codecs_tblscalars2( int * count ); /* the BLOCK unit (blockdemo): open one image at the extent and pointer the * caller claims, and read every row out of the descriptors */ @@ -101,6 +134,8 @@ int conformance_block_dump( const char * name, const uint8_t * data, size_t byte * times, and the noise would bury the one message that matters. It is a flag * rather than a redirect because the sanitizers write to stderr too. */ extern int conformance_quiet; +int conformance_cook_open_reason(const char * root,const uint8_t * data,size_t bytes,int64_t extent,int pointer,int * reason); +int conformance_block_open_reason(const char * name,const uint8_t * data,size_t bytes,int64_t extent,int pointer,int * reason); int conformance_cook_dump( const char * root, const uint8_t * data, size_t bytes, ConformanceText * out ); int conformance_cook_open( const char * root, const uint8_t * data, size_t bytes, int64_t extent, int pointer ); diff --git a/test/conformance/c/main.c b/test/conformance/c/main.c index fe0a6c8ba..89acc4402 100644 --- a/test/conformance/c/main.c +++ b/test/conformance/c/main.c @@ -149,11 +149,31 @@ static int spill( const char * dir, const char * name, const void * data, size_t typedef const ConformanceCodec * ( *UnitFn )( int * count ); static const UnitFn units[] = { + conformance_codecs_mapdemo, + conformance_codecs_listdemo, + conformance_codecs_armdemo, + conformance_codecs_tblw1, + conformance_codecs_tblw2, + conformance_codecs_messagedemo, + conformance_codecs_tblm1, + conformance_codecs_tblm2, + conformance_codecs_tbla1, + conformance_codecs_tbla2, + conformance_codecs_tblk1, + conformance_codecs_tblk2, + conformance_codecs_tblr1, + conformance_codecs_tblr2, + conformance_codecs_backenddemo, + conformance_codecs_vocabdemo, + conformance_codecs_vocab9demo, conformance_codecs_tabledemo, conformance_codecs_tblv1, conformance_codecs_tblv2, conformance_codecs_tblp1, - conformance_codecs_tblp3 + conformance_codecs_tblp3, + conformance_codecs_widedemo, + conformance_codecs_scalars, + conformance_codecs_graphdemo, conformance_codecs_blobdemo, conformance_codecs_tblg1, conformance_codecs_tblp2, conformance_codecs_streamdemo, conformance_codecs_tblscalars2 }; static const ConformanceCodec * find_codec( const char * unit, const char * root ) @@ -240,7 +260,7 @@ static int surface_report( const char * out ) void * value; ConformanceReport report; char text[128]; - int n, ok; + int n; if ( strcmp( f->field[0], "report" ) != 0 ) { continue; } codec = find_codec( f->field[2], f->field[3] ); if ( codec == NULL ) { if ( !spill_absent( out, f->field[1] ) ) { return 1; } continue; } @@ -248,11 +268,11 @@ static int surface_report( const char * out ) if ( wire == NULL ) { fprintf( stderr, "driver: cannot read %s\n", f->field[4] ); return 1; } value = codec->storage(); memset( &report, 0, sizeof( report ) ); - ok = codec->load( value, wire, (int64_t) bytes, &report ); + codec->load( value, wire, (int64_t) bytes, &report ); free( wire ); - n = snprintf( text, sizeof( text ), "%d,%d,%d,%d,%s\n", - report.unknown, report.kind_mismatch, report.clamped, report.duplicate, - ( report.malformed || !ok ) ? "true" : "false" ); + n = snprintf( text, sizeof( text ), "%d,%d,%d,%d,%d,%s,%s\n", + report.unknown, report.kind_mismatch, report.widened, report.clamped, report.duplicate, + report.malformed ? "true" : "false", report.refused ? "refused" : "read" ); if ( !spill( out, f->field[1], text, (size_t) n ) ) { return 1; } } return 0; @@ -273,7 +293,7 @@ static int surface_json_read( const char * out ) if ( strcmp( f->field[0], "instance" ) != 0 || no_text( f ) ) { continue; } codec = find_codec( f->field[2], f->field[3] ); /* the C port carries no text form for a pointered unit (16.7), and says so per case */ - if ( codec == NULL ) { if ( !spill_absent( out, f->field[1] ) ) { return 1; } continue; } + if ( codec == NULL || codec->from_json == NULL ) { if ( !spill_absent( out, f->field[1] ) ) { return 1; } continue; } snprintf( path, sizeof( path ), "testdata/conformance/tables/json/%s.json", f->field[1] ); text = slurp( path, &bytes ); if ( text == NULL ) { fprintf( stderr, "driver: cannot read %s\n", path ); return 1; } @@ -304,7 +324,7 @@ static int surface_json_write( const char * out ) if ( strcmp( f->field[0], "instance" ) != 0 || no_text( f ) ) { continue; } codec = find_codec( f->field[2], f->field[3] ); snprintf( name, sizeof( name ), "%s.json", f->field[1] ); - if ( codec == NULL ) { if ( !spill_absent( out, name ) ) { return 1; } continue; } + if ( codec == NULL || codec->to_json == NULL ) { if ( !spill_absent( out, name ) ) { return 1; } continue; } wire = slurp( f->field[4], &bytes ); if ( wire == NULL ) { fprintf( stderr, "driver: cannot read %s\n", f->field[4] ); return 1; } value = codec->storage(); @@ -342,7 +362,7 @@ static int surface_json_hostile( const char * out ) int n, ok; if ( strcmp( f->field[0], "json-hostile" ) != 0 ) { continue; } codec = find_codec( f->field[2], f->field[3] ); - if ( codec == NULL ) { if ( !spill_absent( out, f->field[1] ) ) { return 1; } continue; } + if ( codec == NULL || codec->from_json == NULL ) { if ( !spill_absent( out, f->field[1] ) ) { return 1; } continue; } /* the tree is what `schema pack` reads, so the text is /.json (§17) */ snprintf( path, sizeof( path ), "%s/%s.json", f->field[4], f->field[3] ); text = slurp( path, &bytes ); @@ -357,8 +377,8 @@ static int surface_json_hostile( const char * out ) } else { - n = snprintf( verdict, sizeof( verdict ), "%d,%d,%d,%d,false\n", - report.unknown, report.kind_mismatch, report.clamped, report.duplicate ); + n = snprintf( verdict, sizeof( verdict ), "%d,%d,%d,%d,%d,false,read\n", + report.unknown, report.kind_mismatch, report.widened, report.clamped, report.duplicate ); } if ( !spill( out, f->field[1], verdict, (size_t) n ) ) { return 1; } } @@ -472,6 +492,35 @@ static int surface_cook_foreign( const char * out ) } +static int surface_cook_write(const char * out) +{ + int i,j; + for(i=0;ifield[0],"cook-write")!=0) { continue; } + for(j=0;jfield[1])==0) { instance=&lines[j]; break; } + } + if(instance==NULL) { return 1; } + codec=find_codec(instance->field[2],instance->field[3]); + if(codec==NULL) { return 2; } + wire=slurp(instance->field[4],&wire_bytes); if(wire==NULL) { return 1; } + value=codec->storage(); + if(!codec->load(value,wire,(int64_t)wire_bytes,&report)) { free(wire); return 1; } free(wire); + need=codec->cook_measure(value); if(need<=0) { return 1; } + file=(uint8_t *)malloc((size_t)need); if(file==NULL) { return 1; } + for(big=0;big<2;big++) { + char name[1024]; int ok=codec->cook(value,file,(uint64_t)need,big); + snprintf(name,sizeof(name),"%s%s",f->field[1],big ? "-be" : ""); + if(!ok || !spill(out,name,file,(size_t)need)) { free(file); return 1; } + } + free(file); + } + return 0; +} + static int surface_cook( const char * out ) { int i; @@ -531,6 +580,59 @@ static int surface_forgery( const char * out, const char * kind ) return 0; } +static int surface_reason(const char * out,const char * kind) +{ + static const char * names[]={"ok","not_a_cook","foreign_order","wrong_build_version","reserved_not_zero","bad_alignment","truncated","unaligned_base","bad_layout","unknown_form","count_over_length","count_over_extent_cap","blob_over_size_cap","data_cycle"}; + int i,j; + for(i=0;ifield[0],"refusal")!=0 || r->count<3 || strcmp(r->field[2],kind)!=0) { continue; } + for(j=0;jfield[1])==0) { f=&lines[j]; break; } } + if(f==NULL) { return 1; } data=slurp(f->field[4],&bytes);if(data==NULL) { return 1; } + extent=(int64_t)strtoll(f->field[5],NULL,0);pointer=strcmp(f->field[6],"null")==0 ? -1 : (int)strtol(f->field[6],NULL,0); + opened=strcmp(kind,"cook")==0 ? conformance_cook_open_reason(f->field[3],data,bytes,extent,pointer,&reason) : conformance_block_open_reason(f->field[3],data,bytes,extent,pointer,&reason); + free(data); if(reason<0 || reason>13) { return 1; } + snprintf(answer,sizeof(answer),"%s\n",opened ? "ok" : names[reason]); + if(!spill(out,r->field[1],answer,strlen(answer))) { return 1; } + } + return 0; +} + +static int surface_message(const char * out) +{ + int i,j; + for(i=0;ifield[0],"message")!=0)continue; + for(j=0;jfield[2])){connection=lines+j;break;} + if(!connection){return 1;}codec=find_codec(connection->field[2],f->field[3]);if(!codec)return 1; + announcement=slurp(connection->field[4],&an);message=slurp(f->field[5],&n); + if(!announcement||!message){free(announcement);free(message);return 1;} + ok=codec->message(announcement,(int64_t)an,message,(int64_t)n,&answer,&size,&report); + if(ok)ok=spill(out,f->field[1],answer,(size_t)size); + free(answer);free(message);free(announcement); + if(!ok){fprintf(stderr,"message %s failed: malformed=%d refused=%d unknown=%d mismatch=%d\n",f->field[1],report.malformed,report.refused,report.unknown,report.kind_mismatch);return 1;} + } + return 0; +} + +static int surface_retain(const char * out,int saved) +{ + int i,j; + for(i=0;ifield[0],"retain-message"),shift=message;uint8_t * a=NULL,*wire,*answer=NULL;size_t an=0,n;int counters[4]={0},ok;int64_t size=0,ids;char summary[128]; + if(!message&&strcmp(f->field[0],"retain"))continue; + if(strcmp(f->field[2+shift],"tblrt1")||strcmp(f->field[3+shift],"Node"))return 1; + if(message){for(j=0;jfield[2])){a=slurp(lines[j].field[4],&an);break;}if(!a)return 1;} + wire=slurp(f->field[4+shift],&n);if(!wire){free(a);return 1;}ids=!strcmp(f->field[6+shift],"full")?-1:strtoll(f->field[6+shift],NULL,10); + ok=conformance_retain(message,a,(int64_t)an,wire,(int64_t)n,!strcmp(f->field[5+shift],"short"),ids,counters,&answer,&size); + if(ok){if(saved)ok=spill(out,f->field[1],answer,(size_t)size);else{int length=snprintf(summary,sizeof(summary),"%d,%d,%d %d\n",counters[0],counters[1],counters[2],counters[3]);ok=spill(out,f->field[1],summary,(size_t)length);}} + free(answer);free(wire);free(a);if(!ok){fprintf(stderr,"retain %s failed\n",f->field[1]);return 1;} + } + return 0; +} + int main( int argc, char ** argv ) { const char * surface; @@ -544,10 +646,7 @@ int main( int argc, char ** argv ) surface = argv[2]; if ( strcmp( surface, "list" ) == 0 ) { - /* the five WIRE-CARRYING surfaces are ABSENT: this port writes the wire's - PREVIOUS form and the corpus is pinned in the id-table form - (docs/SPEC-TABLES.md §3). schema#512 is the port's row. */ - printf( "cook\ncook-foreign\nblock\nblock-foreign\nblock-dump\nforgery\ncook-forgery\n" ); + printf( "wire\nmessage\nretain\nretain-save\nreport\njson-read\njson-write\njson-hostile\ncook\ncook-write\ncook-reason\nblock-reason\ncook-foreign\nblock\nblock-foreign\nblock-dump\nforgery\ncook-forgery\n" ); return 0; } if ( argc < 4 ) @@ -556,11 +655,17 @@ int main( int argc, char ** argv ) return 2; } out = argv[3]; + if(!strcmp(surface,"retain"))return surface_retain(out,0); + if(!strcmp(surface,"retain-save"))return surface_retain(out,1); + if ( strcmp( surface, "message" ) == 0 ) { return surface_message(out); } if ( strcmp( surface, "wire" ) == 0 ) { return surface_wire( out ); } if ( strcmp( surface, "report" ) == 0 ) { return surface_report( out ); } if ( strcmp( surface, "json-read" ) == 0 ) { return surface_json_read( out ); } if ( strcmp( surface, "json-write" ) == 0 ) { return surface_json_write( out ); } if ( strcmp( surface, "json-hostile" ) == 0 ) { return surface_json_hostile( out ); } + if ( strcmp( surface, "cook-reason" ) == 0 ) { return surface_reason(out,"cook"); } + if ( strcmp( surface, "block-reason" ) == 0 ) { return surface_reason(out,"block"); } + if ( strcmp( surface, "cook-write" ) == 0 ) { return surface_cook_write(out); } if ( strcmp( surface, "cook" ) == 0 ) { return surface_cook( out ); } if ( strcmp( surface, "block" ) == 0 ) { return surface_block( out ); } if ( strcmp( surface, "cook-foreign" ) == 0 ) { return surface_cook_foreign( out ); } diff --git a/test/conformance/c/retain.c b/test/conformance/c/retain.c new file mode 100644 index 000000000..e29d6576a --- /dev/null +++ b/test/conformance/c/retain.c @@ -0,0 +1,24 @@ +#include "driver.h" +#include "RT1Table.h" + +/* Each body owns its bounded byte store and retained-ID list. */ +int conformance_retain(int message,const uint8_t * announcement,int64_t announcement_bytes,const uint8_t * wire,int64_t bytes,int short_buffer,int64_t id_capacity,int * counters,uint8_t ** output,int64_t * output_bytes) +{ + enum { roomy=1<<20, batch_max=256 }; + TableMessageEntry entries[256];TableVocabulary vocabulary=table_vocabulary(entries,256); + TableRetain retains[batch_max];const Node * roots[batch_max];TableReport report={0},save={0}; + uint8_t * region=NULL;int64_t need,count=1,i,total=0;int ok=0; + memset(retains,0,sizeof(retains));*output=NULL;*output_bytes=0; + if(id_capacity<0)id_capacity=roomy/8; + if(id_capacity>INT32_MAX)return 0; + if(message){if(!announce_read(&vocabulary,announcement,announcement_bytes,&report)||bytes<2)return 0;count=(int64_t)wire[1]+1;need=node_load_measure_messages(&vocabulary,wire,bytes);} + else need=node_load_measure(wire,bytes); + if(need<0){return 0;}region=(uint8_t *)malloc((size_t)need);if(!region)return 0; + for(i=0;iINT64_MAX-total)goto done;grown=(uint8_t *)realloc(*output,(size_t)(total+size));if(!grown)goto done;*output=grown;if(node_save_retain(roots[i],retains+i,grown+total,size,&save)!=size)goto done;total+=size;} + counters[3]=save.retain_lost;*output_bytes=total;ok=1; + done:for(i=0;iclamped = ( from ).clamped; \ ( to )->duplicate = ( from ).duplicate; \ ( to )->malformed = ( from ).malformed; \ + ( to )->refused = ( from ).refused; \ + ( to )->widened = ( from ).widened; \ } while ( 0 ) #define SCHEMA_CONFORMANCE_CODEC( TYPE, FN ) \ + static int64_t schema_conformance_message_fuzz_##TYPE(const uint8_t * wire,int64_t bytes,uint8_t ** saved,ConformanceReport * report,int * loaded,int64_t * extent) \ + { \ + static TableMessageEntry entries[kTableMessageEntriesHere];static TableVocabulary vocabulary; \ + TableReport inner={0},ignored={0};int64_t count=256,size=-1;int ok; \ + if(!vocabulary.announced){vocabulary=table_vocabulary(entries,kTableMessageEntriesHere);if(!announce_read(&vocabulary,kTableAnnounce,kTableAnnounceBytes,&inner))return -1;} \ + static TYPE values[256];int i; \ + for(i=0;i<256;i++)FN##_reset(values+i); \ + ok=FN##_load_messages(values,&count,&vocabulary,wire,bytes,&inner);*extent=-1;*loaded=1; \ + if(!ok&&!inner.refused)inner.malformed=1; \ + size=FN##_measure_messages(values,1,&ignored); \ + if(size>=0){*saved=(uint8_t *)malloc((size_t)size);if(!*saved)size=-1;else size=FN##_save_messages(values,1,*saved,size,&ignored);} \ + SCHEMA_CONFORMANCE_COPY_REPORT(inner,report);return size; \ + } \ + static int schema_conformance_message_##TYPE(const uint8_t * a,int64_t an,const uint8_t * b,int64_t n,uint8_t ** out,int64_t * size,ConformanceReport * report) \ + { \ + TableMessageEntry entries[kTableMessageEntriesHere];TableVocabulary v=table_vocabulary(entries,kTableMessageEntriesHere); \ + TableReport inner={0};int64_t count=1;int ok=0;TYPE value; \ + if(!announce_read(&v,a,an,&inner))goto done; \ + if(!FN##_load_messages(&value,&count,&v,b,n,&inner))goto done; \ + *size=FN##_measure_messages(&value,count,&inner);if(*size<0)goto done; \ + *out=(uint8_t *)malloc((size_t)*size);if(!*out)goto done; \ + ok=FN##_save_messages(&value,count,*out,*size,&inner)==*size; \ + done:;SCHEMA_CONFORMANCE_COPY_REPORT(inner,report);return ok; \ + } \ static TYPE schema_conformance_storage_##TYPE; \ static void * schema_conformance_make_##TYPE( void ) \ { \ @@ -53,6 +79,10 @@ { \ return FN##_save( (const TYPE *) value, buffer, capacity ); \ } \ + static int64_t schema_conformance_cook_measure_##TYPE(const void * value) \ + { return FN##_cook_measure((const TYPE *)value); } \ + static int schema_conformance_cook_##TYPE(const void * value,void * buffer,uint64_t capacity,int big) \ + { return FN##_cook((const TYPE *)value,buffer,capacity,big ? TableByteOrder_Big : TableByteOrder_Little); } \ static int schema_conformance_from_json_##TYPE( void * value, const char * text, int64_t bytes, ConformanceReport * report ) \ { \ TableReport inner; \ @@ -68,9 +98,91 @@ : FN##_to_json_measure( (const TYPE *) value ); \ } + +/* Variable batch_roots are loaded into driver-owned regions. The generated load + itself neither allocates nor follows a reference. */ +#define SCHEMA_CONFORMANCE_GRAPH_CODEC( TYPE, FN ) \ + static int64_t schema_conformance_retain_fuzz_##TYPE(const uint8_t * wire,int64_t bytes,uint8_t ** saved,ConformanceReport * report,int * loaded,int64_t * extent) \ + { \ + static uint8_t store[1<<20];static TableRetainId ids[(1<<20)/8];TableRetain retain={0};TableReport inner={0};uint8_t * region;const TYPE * root;int64_t size=-1; \ + retain.bytes=store;retain.capacity=sizeof(store);retain.ids=ids;retain.id_capacity=(1<<20)/8;*extent=FN##_load_measure(wire,bytes);region=(uint8_t *)malloc((size_t)(*extent>0?*extent:1));if(!region)return -1; \ + root=FN##_load_retain(region,*extent,wire,bytes,&retain,&inner);*loaded=root!=NULL;if(root)size=FN##_measure_retain(root,&retain); \ + if(size>=0){*saved=(uint8_t *)malloc((size_t)(size?size:1));if(!*saved)size=-1;else size=FN##_save_retain(root,&retain,*saved,size,&inner);}free(region);SCHEMA_CONFORMANCE_COPY_REPORT(inner,report);report->retained=inner.retained;report->retain_lost=inner.retain_lost;return size; \ + } \ + static int64_t schema_conformance_message_fuzz_##TYPE(const uint8_t * wire,int64_t bytes,uint8_t ** saved,ConformanceReport * report,int * loaded,int64_t * extent) \ + { \ + static TableMessageEntry entries[kTableMessageEntriesHere];static TableVocabulary vocabulary; \ + TableReport inner={0},ignored={0};int64_t count=256,size=-1;int ok; \ + if(!vocabulary.announced){vocabulary=table_vocabulary(entries,kTableMessageEntriesHere);if(!announce_read(&vocabulary,kTableAnnounce,kTableAnnounceBytes,&inner))return -1;} \ + const TYPE * batch_roots[256]={NULL};uint8_t * region; \ + *extent=FN##_load_measure_messages(&vocabulary,wire,bytes);region=(uint8_t *)malloc((size_t)(*extent>0?*extent:1));if(!region)return -1; \ + ok=FN##_load_messages(batch_roots,&count,region,*extent,&vocabulary,wire,bytes,&inner);(void)ok;*loaded=batch_roots[0]!=NULL; \ + if(*loaded)size=FN##_measure_messages(batch_roots,1,&ignored); \ + if(size>=0){*saved=(uint8_t *)malloc((size_t)size);if(!*saved)size=-1;else size=FN##_save_messages(batch_roots,1,*saved,size,&ignored);} \ + free(region); \ + SCHEMA_CONFORMANCE_COPY_REPORT(inner,report);return size; \ + } \ + static int schema_conformance_message_##TYPE(const uint8_t * a,int64_t an,const uint8_t * b,int64_t n,uint8_t ** out,int64_t * size,ConformanceReport * report) \ + { \ + TableMessageEntry entries[kTableMessageEntriesHere];TableVocabulary v=table_vocabulary(entries,kTableMessageEntriesHere); \ + TableReport inner={0};int64_t count=1;int ok=0;const TYPE * batch_roots[1]={NULL};uint8_t * region=NULL;int64_t need; \ + if(!announce_read(&v,a,an,&inner))goto done; \ + need=FN##_load_measure_messages(&v,b,n);if(need<0)goto done;region=(uint8_t *)malloc((size_t)need);if(!region)goto done; \ + if(!FN##_load_messages(batch_roots,&count,region,need,&v,b,n,&inner))goto done; \ + *size=FN##_measure_messages(batch_roots,count,&inner);if(*size<0)goto done; \ + *out=(uint8_t *)malloc((size_t)*size);if(!*out)goto done; \ + ok=FN##_save_messages(batch_roots,count,*out,*size,&inner)==*size; \ + done:free(region);SCHEMA_CONFORMANCE_COPY_REPORT(inner,report);return ok; \ + } \ + typedef struct { TYPE empty; const TYPE * root; uint8_t * region; } schema_conformance_holder_##TYPE; \ + static schema_conformance_holder_##TYPE schema_conformance_storage_##TYPE; \ + static void * schema_conformance_make_##TYPE(void) \ + { \ + schema_conformance_holder_##TYPE * holder=&schema_conformance_storage_##TYPE; \ + free(holder->region); holder->region=NULL; FN##_reset(&holder->empty); holder->root=&holder->empty; \ + return holder; \ + } \ + static int schema_conformance_load_##TYPE(void * value,const uint8_t * bytes,int64_t size,ConformanceReport * report) \ + { \ + schema_conformance_holder_##TYPE * holder=(schema_conformance_holder_##TYPE *)value; \ + TableReport inner; int64_t extent=FN##_load_measure(bytes,size); const TYPE * root; \ + memset(&inner,0,sizeof(inner)); \ + if(extent>0) { holder->region=(uint8_t *)malloc((size_t)extent); } \ + root=FN##_load(holder->region,extent,bytes,size,&inner); \ + if(root!=NULL) { holder->root=root; } \ + SCHEMA_CONFORMANCE_COPY_REPORT(inner,report); return root!=NULL; \ + } \ + static int64_t schema_conformance_load_measure_##TYPE(const uint8_t * wire,int64_t bytes) \ + { return FN##_load_measure(wire,bytes); } \ + static int64_t schema_conformance_measure_##TYPE(const void * value) \ + { return FN##_measure(NULL,((const schema_conformance_holder_##TYPE *)value)->root); } \ + static int64_t schema_conformance_save_##TYPE(const void * value,uint8_t * buffer,int64_t capacity) \ + { return FN##_save(NULL,((const schema_conformance_holder_##TYPE *)value)->root,buffer,capacity); } \ + static int64_t schema_conformance_cook_measure_##TYPE(const void * value) \ + { return FN##_cook_measure(NULL,((const schema_conformance_holder_##TYPE *)value)->root); } \ + static int schema_conformance_cook_##TYPE(const void * value,void * buffer,uint64_t capacity,int big) \ + { return FN##_cook(NULL,((const schema_conformance_holder_##TYPE *)value)->root,buffer,capacity,big ? TableByteOrder_Big : TableByteOrder_Little); } \ + static int schema_conformance_from_json_##TYPE(void * value,const char * text,int64_t bytes,ConformanceReport * report) \ + { \ + schema_conformance_holder_##TYPE * holder=(schema_conformance_holder_##TYPE *)value; \ + TYPE##Builder builder; TableReport inner; int ok; \ + memset(&inner,0,sizeof(inner)); \ + if(!FN##_builder_init(&builder)) { FN##_builder_shutdown(&builder); return 0; } \ + ok=FN##_from_json(&builder,text,bytes,&inner); \ + if(ok) { ok=FN##_builder_lock(&builder); } \ + if(ok) { free(holder->region); holder->region=builder.region; holder->root=(const TYPE *)(const void *)builder.region; builder.region=NULL; } \ + FN##_builder_shutdown(&builder); SCHEMA_CONFORMANCE_COPY_REPORT(inner,report); return ok; \ + } \ + static int64_t schema_conformance_to_json_##TYPE(const void * value,char * buffer,int64_t capacity) \ + { return FN##_to_json(((const schema_conformance_holder_##TYPE *)value)->root,buffer,capacity); } + +#define SCHEMA_CONFORMANCE_GRAPH_ROW( UNIT, TYPE ) \ + { #UNIT, #TYPE, schema_conformance_message_fuzz_##TYPE, schema_conformance_message_##TYPE, schema_conformance_load_##TYPE, schema_conformance_measure_##TYPE, \ + schema_conformance_save_##TYPE, schema_conformance_from_json_##TYPE, schema_conformance_to_json_##TYPE, schema_conformance_make_##TYPE, schema_conformance_load_measure_##TYPE, schema_conformance_cook_measure_##TYPE, schema_conformance_cook_##TYPE, schema_conformance_retain_fuzz_##TYPE } + #define SCHEMA_CONFORMANCE_ROW( UNIT, TYPE ) \ - { #UNIT, #TYPE, schema_conformance_load_##TYPE, schema_conformance_measure_##TYPE, \ + { #UNIT, #TYPE, schema_conformance_message_fuzz_##TYPE, schema_conformance_message_##TYPE, schema_conformance_load_##TYPE, schema_conformance_measure_##TYPE, \ schema_conformance_save_##TYPE, schema_conformance_from_json_##TYPE, \ - schema_conformance_to_json_##TYPE, schema_conformance_make_##TYPE } + schema_conformance_to_json_##TYPE, schema_conformance_make_##TYPE, NULL, schema_conformance_cook_measure_##TYPE, schema_conformance_cook_##TYPE, NULL } #endif /* SCHEMA_CONFORMANCE_UNIT_H */ diff --git a/test/conformance/c/unit_backenddemo.c b/test/conformance/c/unit_backenddemo.c new file mode 100644 index 000000000..3cc8a8091 --- /dev/null +++ b/test/conformance/c/unit_backenddemo.c @@ -0,0 +1,14 @@ +#include "BackendTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( LoginRequest, login_request ) +SCHEMA_CONFORMANCE_CODEC( MatchResult, match_result ) +SCHEMA_CONFORMANCE_CODEC( StorePurchase, store_purchase ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( backenddemo, LoginRequest ), + SCHEMA_CONFORMANCE_ROW( backenddemo, MatchResult ), + SCHEMA_CONFORMANCE_ROW( backenddemo, StorePurchase ) +}; +const ConformanceCodec * conformance_codecs_backenddemo( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_blobdemo.c b/test/conformance/c/unit_blobdemo.c new file mode 100644 index 000000000..f69959cf8 --- /dev/null +++ b/test/conformance/c/unit_blobdemo.c @@ -0,0 +1,8 @@ +#include "AssetsTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Catalog,catalog) +const ConformanceCodec * conformance_codecs_blobdemo(int * count) +{ + static const ConformanceCodec codecs[]={SCHEMA_CONFORMANCE_GRAPH_ROW(blobdemo,Catalog)}; + *count=(int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_blockdemo.c b/test/conformance/c/unit_blockdemo.c index 44067c575..54b61b5cc 100644 --- a/test/conformance/c/unit_blockdemo.c +++ b/test/conformance/c/unit_blockdemo.c @@ -210,7 +210,7 @@ static int dump_block( ConformanceText * out, const uint8_t * base, const TableB /* ---- the two entry points main.c names ---- */ -int conformance_block_open( const char * name, const uint8_t * data, size_t bytes, int64_t extent, int pointer ) +int conformance_block_open_reason( const char * name, const uint8_t * data, size_t bytes, int64_t extent, int pointer,int * reason ) { ConformanceBuffer buffer; int opened = 0; @@ -218,12 +218,12 @@ int conformance_block_open( const char * name, const uint8_t * data, size_t byte if ( strncmp( name, "block_render", 12 ) == 0 ) { RenderFrameBlock block; - opened = render_frame_block_open( &block, buffer.base, buffer.bytes ); + opened = render_frame_block_open_ex( &block, buffer.base, buffer.bytes,reason ); } else if ( strncmp( name, "block_padded", 12 ) == 0 ) { PaddedFrameBlock block; - opened = padded_frame_block_open( &block, buffer.base, buffer.bytes ); + opened = padded_frame_block_open_ex( &block, buffer.base, buffer.bytes,reason ); } else { @@ -259,3 +259,6 @@ int conformance_block_dump( const char * name, const uint8_t * data, size_t byte conformance_buffer_destroy( &buffer ); return ok; } + +int conformance_block_open(const char * name,const uint8_t * data,size_t bytes,int64_t extent,int pointer) +{ return conformance_block_open_reason(name,data,bytes,extent,pointer,NULL); } diff --git a/test/conformance/c/unit_graphdemo.c b/test/conformance/c/unit_graphdemo.c index aaef4d2c3..b7e0aa5aa 100644 --- a/test/conformance/c/unit_graphdemo.c +++ b/test/conformance/c/unit_graphdemo.c @@ -21,7 +21,7 @@ /* ---- the roots a cook may be opened at ---- */ -typedef const void * ( *CookOpenFn )( const void * bytes, uint64_t length ); +typedef const void * ( *CookOpenFn )( const void * bytes, uint64_t length,TableRefuseReason * reason ); typedef const TableTypeInfo * ( *CookTypeFn )( void ); typedef struct CookRoot @@ -31,16 +31,16 @@ typedef struct CookRoot CookTypeFn type; } CookRoot; -static const void * open_scene( const void * b, uint64_t n ) { return scene_open( b, n ); } -static const void * open_depot( const void * b, uint64_t n ) { return depot_open( b, n ); } -static const void * open_album( const void * b, uint64_t n ) { return album_open( b, n ); } -static const void * open_tree( const void * b, uint64_t n ) { return tree_node_open( b, n ); } -static const void * open_list( const void * b, uint64_t n ) { return list_node_open( b, n ); } -static const void * open_marker( const void * b, uint64_t n ) { return marker_open( b, n ); } +static const void * open_scene( const void * b, uint64_t n,TableRefuseReason * reason ) { return scene_open_ex( b, n,reason ); } +static const void * open_depot( const void * b, uint64_t n,TableRefuseReason * reason ) { return depot_open_ex( b, n,reason ); } +static const void * open_album( const void * b, uint64_t n,TableRefuseReason * reason ) { return album_open_ex( b, n,reason ); } +static const void * open_tree( const void * b, uint64_t n,TableRefuseReason * reason ) { return tree_node_open_ex( b, n,reason ); } +static const void * open_list( const void * b, uint64_t n,TableRefuseReason * reason ) { return list_node_open_ex( b, n,reason ); } +static const void * open_marker( const void * b, uint64_t n,TableRefuseReason * reason ) { return marker_open_ex( b, n,reason ); } /* the FIXED class: a cook of one is ONE REGION OF ONE NODE (§7), and it is the same header match. */ -static const void * open_settings( const void * b, uint64_t n ) { return settings_open( b, n ); } -static const void * open_stamp( const void * b, uint64_t n ) { return stamp_open( b, n ); } +static const void * open_settings( const void * b, uint64_t n,TableRefuseReason * reason ) { return settings_open_ex( b, n,reason ); } +static const void * open_stamp( const void * b, uint64_t n,TableRefuseReason * reason ) { return stamp_open_ex( b, n,reason ); } static const CookRoot roots[] = { { "Scene", open_scene, scene_table_type }, @@ -365,7 +365,7 @@ int conformance_cook_dump( const char * root_name, const uint8_t * data, size_t int ok; if ( root == NULL ) { fprintf( stderr, "driver: no cook root named %s\n", root_name ); return 0; } if ( !conformance_buffer_create( &buffer, data, bytes, -1, 0 ) ) { return 0; } - region = (const uint8_t *) root->open( buffer.base, (uint64_t) buffer.bytes ); + region = (const uint8_t *) root->open( buffer.base, (uint64_t) buffer.bytes,NULL ); if ( region == NULL ) { fprintf( stderr, "driver: the cook at root %s does not open\n", root_name ); @@ -384,14 +384,22 @@ int conformance_cook_dump( const char * root_name, const uint8_t * data, size_t return ok; } -int conformance_cook_open( const char * root_name, const uint8_t * data, size_t bytes, int64_t extent, int pointer ) +int conformance_cook_open_reason( const char * root_name, const uint8_t * data, size_t bytes, int64_t extent, int pointer,int * reason ) { const CookRoot * root = find_root( root_name ); ConformanceBuffer buffer; int opened; if ( root == NULL ) { fprintf( stderr, "driver: no cook root named %s\n", root_name ); exit( 1 ); } if ( !conformance_buffer_create( &buffer, data, bytes, extent, pointer ) ) { return 0; } - opened = root->open( buffer.base, (uint64_t) buffer.bytes ) != NULL; + opened = root->open( buffer.base, (uint64_t) buffer.bytes,reason ) != NULL; conformance_buffer_destroy( &buffer ); return opened; } + +SCHEMA_CONFORMANCE_GRAPH_CODEC(Scene,scene) +static const ConformanceCodec graph_codecs[] = { SCHEMA_CONFORMANCE_GRAPH_ROW(graphdemo,Scene) }; +const ConformanceCodec * conformance_codecs_graphdemo(int * count) +{ *count=(int)(sizeof(graph_codecs)/sizeof(graph_codecs[0])); return graph_codecs; } + +int conformance_cook_open(const char * root,const uint8_t * data,size_t bytes,int64_t extent,int pointer) +{ return conformance_cook_open_reason(root,data,bytes,extent,pointer,NULL); } diff --git a/test/conformance/c/unit_messagedemo.c b/test/conformance/c/unit_messagedemo.c new file mode 100644 index 000000000..7e59ccfe4 --- /dev/null +++ b/test/conformance/c/unit_messagedemo.c @@ -0,0 +1,12 @@ +#include "MessagesTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( ToolMessage, tool_message ) +SCHEMA_CONFORMANCE_CODEC( Edit, edit ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( messagedemo, ToolMessage ), + SCHEMA_CONFORMANCE_ROW( messagedemo, Edit ) +}; +const ConformanceCodec * conformance_codecs_messagedemo( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_scalars.c b/test/conformance/c/unit_scalars.c new file mode 100644 index 000000000..23a199f8f --- /dev/null +++ b/test/conformance/c/unit_scalars.c @@ -0,0 +1,13 @@ +/* The wide scalar and fixed-point corpus, under the same erased C surface. */ +#include "ScalarsTable.h" +#include "unit.h" + +SCHEMA_CONFORMANCE_CODEC( SimState, sim_state ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( scalars, SimState ) +}; +const ConformanceCodec * conformance_codecs_scalars( int * count ) +{ + *count = (int) ( sizeof( codecs ) / sizeof( codecs[0] ) ); + return codecs; +} diff --git a/test/conformance/c/unit_streamdemo.c b/test/conformance/c/unit_streamdemo.c new file mode 100644 index 000000000..48e65e2ee --- /dev/null +++ b/test/conformance/c/unit_streamdemo.c @@ -0,0 +1,8 @@ +#include "StreamTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Feed,feed) +const ConformanceCodec * conformance_codecs_streamdemo(int * count) +{ + static const ConformanceCodec codecs[]={SCHEMA_CONFORMANCE_GRAPH_ROW(streamdemo,Feed)}; + *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} diff --git a/test/conformance/c/unit_tbla1.c b/test/conformance/c/unit_tbla1.c new file mode 100644 index 000000000..06eb9718f --- /dev/null +++ b/test/conformance/c/unit_tbla1.c @@ -0,0 +1,10 @@ +#include "A1Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Root, root ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tbla1, Root ) +}; +const ConformanceCodec * conformance_codecs_tbla1( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_tbla2.c b/test/conformance/c/unit_tbla2.c new file mode 100644 index 000000000..818ed3c28 --- /dev/null +++ b/test/conformance/c/unit_tbla2.c @@ -0,0 +1,10 @@ +#include "A2Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Root, root ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tbla2, Root ) +}; +const ConformanceCodec * conformance_codecs_tbla2( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_tblg1.c b/test/conformance/c/unit_tblg1.c new file mode 100644 index 000000000..c9eabf28f --- /dev/null +++ b/test/conformance/c/unit_tblg1.c @@ -0,0 +1,8 @@ +#include "G1Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Guarded,guarded) +const ConformanceCodec * conformance_codecs_tblg1(int * count) +{ + static const ConformanceCodec codecs[]={SCHEMA_CONFORMANCE_GRAPH_ROW(tblg1,Guarded)}; + *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} diff --git a/test/conformance/c/unit_tblk1.c b/test/conformance/c/unit_tblk1.c new file mode 100644 index 000000000..9f3291bce --- /dev/null +++ b/test/conformance/c/unit_tblk1.c @@ -0,0 +1,10 @@ +#include "K1Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Root, root ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tblk1, Root ) +}; +const ConformanceCodec * conformance_codecs_tblk1( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_tblk2.c b/test/conformance/c/unit_tblk2.c new file mode 100644 index 000000000..a445dafb3 --- /dev/null +++ b/test/conformance/c/unit_tblk2.c @@ -0,0 +1,10 @@ +#include "K2Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Root, root ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tblk2, Root ) +}; +const ConformanceCodec * conformance_codecs_tblk2( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_tblm1.c b/test/conformance/c/unit_tblm1.c new file mode 100644 index 000000000..531c6d4e4 --- /dev/null +++ b/test/conformance/c/unit_tblm1.c @@ -0,0 +1,10 @@ +#include "M1Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Msg, msg ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tblm1, Msg ) +}; +const ConformanceCodec * conformance_codecs_tblm1( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_tblm2.c b/test/conformance/c/unit_tblm2.c new file mode 100644 index 000000000..edb27b00a --- /dev/null +++ b/test/conformance/c/unit_tblm2.c @@ -0,0 +1,10 @@ +#include "M2Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Msg, msg ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tblm2, Msg ) +}; +const ConformanceCodec * conformance_codecs_tblm2( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_tblp2.c b/test/conformance/c/unit_tblp2.c new file mode 100644 index 000000000..3ea597e3e --- /dev/null +++ b/test/conformance/c/unit_tblp2.c @@ -0,0 +1,8 @@ +#include "P2Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Chain,chain) +const ConformanceCodec * conformance_codecs_tblp2(int * count) +{ + static const ConformanceCodec codecs[]={SCHEMA_CONFORMANCE_GRAPH_ROW(tblp2,Chain)}; + *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} diff --git a/test/conformance/c/unit_tblr1.c b/test/conformance/c/unit_tblr1.c new file mode 100644 index 000000000..b67d56f69 --- /dev/null +++ b/test/conformance/c/unit_tblr1.c @@ -0,0 +1,10 @@ +#include "R1Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Cfg, cfg ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tblr1, Cfg ) +}; +const ConformanceCodec * conformance_codecs_tblr1( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_tblr2.c b/test/conformance/c/unit_tblr2.c new file mode 100644 index 000000000..74850f1c2 --- /dev/null +++ b/test/conformance/c/unit_tblr2.c @@ -0,0 +1,10 @@ +#include "R2Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Cfg, cfg ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tblr2, Cfg ) +}; +const ConformanceCodec * conformance_codecs_tblr2( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_tblscalars2.c b/test/conformance/c/unit_tblscalars2.c new file mode 100644 index 000000000..604b8be82 --- /dev/null +++ b/test/conformance/c/unit_tblscalars2.c @@ -0,0 +1,12 @@ +#include "Scalars2Table.h" +#include "unit.h" + +SCHEMA_CONFORMANCE_CODEC( SimState, sim_state ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( tblscalars2, SimState ) +}; +const ConformanceCodec * conformance_codecs_tblscalars2( int * count ) +{ + *count = (int) ( sizeof( codecs ) / sizeof( codecs[0] ) ); + return codecs; +} diff --git a/test/conformance/c/unit_tblw1.c b/test/conformance/c/unit_tblw1.c new file mode 100644 index 000000000..e35728f64 --- /dev/null +++ b/test/conformance/c/unit_tblw1.c @@ -0,0 +1,8 @@ +#include "W1Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Fleet,fleet) +const ConformanceCodec * conformance_codecs_tblw1(int * count) +{ + static const ConformanceCodec codecs[]={SCHEMA_CONFORMANCE_GRAPH_ROW(tblw1,Fleet)}; + *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} diff --git a/test/conformance/c/unit_tblw2.c b/test/conformance/c/unit_tblw2.c new file mode 100644 index 000000000..a5a977699 --- /dev/null +++ b/test/conformance/c/unit_tblw2.c @@ -0,0 +1,8 @@ +#include "W2Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_GRAPH_CODEC(Fleet,fleet) +const ConformanceCodec * conformance_codecs_tblw2(int * count) +{ + static const ConformanceCodec codecs[]={SCHEMA_CONFORMANCE_GRAPH_ROW(tblw2,Fleet)}; + *count=(int)(sizeof(codecs)/sizeof(codecs[0]));return codecs; +} diff --git a/test/conformance/c/unit_vocab9demo.c b/test/conformance/c/unit_vocab9demo.c new file mode 100644 index 000000000..57e0cec20 --- /dev/null +++ b/test/conformance/c/unit_vocab9demo.c @@ -0,0 +1,12 @@ +#include "Vocab9Table.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Wide00, wide00 ) +SCHEMA_CONFORMANCE_CODEC( Wide19, wide19 ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( vocab9demo, Wide00 ), + SCHEMA_CONFORMANCE_ROW( vocab9demo, Wide19 ) +}; +const ConformanceCodec * conformance_codecs_vocab9demo( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_vocabdemo.c b/test/conformance/c/unit_vocabdemo.c new file mode 100644 index 000000000..fef5c0334 --- /dev/null +++ b/test/conformance/c/unit_vocabdemo.c @@ -0,0 +1,12 @@ +#include "VocabTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Wide00, wide00 ) +SCHEMA_CONFORMANCE_CODEC( Wide09, wide09 ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( vocabdemo, Wide00 ), + SCHEMA_CONFORMANCE_ROW( vocabdemo, Wide09 ) +}; +const ConformanceCodec * conformance_codecs_vocabdemo( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/c/unit_widedemo.c b/test/conformance/c/unit_widedemo.c new file mode 100644 index 000000000..8f09c00c2 --- /dev/null +++ b/test/conformance/c/unit_widedemo.c @@ -0,0 +1,12 @@ +#include "CaptionTable.h" +#include "unit.h" +SCHEMA_CONFORMANCE_CODEC( Caption, caption ) +SCHEMA_CONFORMANCE_CODEC( Stamp, stamp ) +static const ConformanceCodec codecs[] = { + SCHEMA_CONFORMANCE_ROW( widedemo, Caption ), + SCHEMA_CONFORMANCE_ROW( widedemo, Stamp ) +}; +const ConformanceCodec * conformance_codecs_widedemo( int * count ) +{ + *count = (int)(sizeof(codecs)/sizeof(codecs[0])); return codecs; +} diff --git a/test/conformance/harness/absence_test.go b/test/conformance/harness/absence_test.go index 846fcedcc..e06e9c4e6 100644 --- a/test/conformance/harness/absence_test.go +++ b/test/conformance/harness/absence_test.go @@ -14,6 +14,7 @@ package main import ( "bytes" "fmt" + "os" "os/exec" "path/filepath" "strings" @@ -56,12 +57,16 @@ func newFakeCorpus(t *testing.T) fakeCorpus { c.json = filepath.Join(c.jsonDir, "alpha.json") writeFile(t, c.wire, "\x01\x02\x03\x04") writeFile(t, c.json, "{\"alpha\":1}\n") - writeFile(t, c.manifest, "unit u tables/examples\ninstance alpha u Root "+c.wire+"\n") + writeFile(t, c.manifest, "unit u tables/examples\ninstance alpha u Root alpha.bin\n") writeFile(t, c.reports, "") m, err := ReadManifest(c.manifest, c.jsonDir) if err != nil { t.Fatal(err) } + // Manifest tokens are whitespace-delimited; this fake driver ignores + // their paths. Attach the fixture's actual host path after parsing so a + // temporary directory with spaces does not change the fixture grammar. + m.Instances[0].Wire = c.wire c.m = m return c } @@ -123,7 +128,15 @@ func (c fakeCorpus) substituted(t *testing.T, lang string, leg fakeLeg) string { script := filepath.Join(dir, "driver") c.driverScript(t, script, leg) path := filepath.Join(dir, "drivers.txt") - writeFile(t, path, lang+" "+script+"\n") + cwd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + command, err := filepath.Rel(cwd, script) + if err != nil { + t.Fatal(err) + } + writeFile(t, path, lang+" "+command+"\n") return path } diff --git a/test/conformance/harness/collections_test.go b/test/conformance/harness/collections_test.go new file mode 100644 index 000000000..781c47e2b --- /dev/null +++ b/test/conformance/harness/collections_test.go @@ -0,0 +1,137 @@ +package main + +// The collection extent contract is independently implemented by the C++ +// reference. Exercise it and C with the shared hostile-wire mutators, including +// exact LoadMeasure answers for partial and duplicated container fields. +import ( + "bytes" + "os" + "path/filepath" + "testing" +) + +func TestCCollectionDifferential(t *testing.T) { cCollectionDifferential(t, "") } + +func TestCCollectionCookDifferential(t *testing.T) { + for _, mode := range []string{"--cook-le", "--cook-be"} { + t.Run(mode, func(t *testing.T) { cCollectionDifferential(t, mode) }) + } +} + +func cCollectionDifferential(t *testing.T, mode string) { + driver, reference := os.Getenv("SCHEMA_C_COLLECTIONS_DRIVER"), os.Getenv("SCHEMA_CPP_COLLECTIONS_DRIVER") + if driver == "" || reference == "" { + t.Skip("run make tables-c-collections-fuzz to build both native legs") + } + t.Chdir(conformanceRoot(t)) + driver += " " + mode + reference += " " + mode + var roots []*wireRoot + var seeds []*wireSeed + var indexes []int + cases := []struct{ unit, root, name string }{ + {"mapdemo", "Depth", "map_depth"}, + {"mapdemo", "Text", "map_text"}, + {"mapdemo", "Cells", "map_cells"}, + {"mapdemo", "Runs", "map_runs"}, + {"mapdemo", "Slots", "map_slots"}, + {"mapdemo", "Spans", "map_spans"}, + {"mapdemo", "Docs", "map_docs"}, + {"mapdemo", "Chunks", "map_chunks"}, + {"mapdemo", "Pairs", "map_pairs"}, + {"mapdemo", "Crews", "map_crews"}, + {"mapdemo", "Trails", "map_trails"}, + {"mapdemo", "Fleet", "map_full"}, + {"mapdemo", "Fleet", "map_empty"}, + {"listdemo", "Save", "list_tables"}, + {"listdemo", "Save", "list_scalars"}, + {"listdemo", "Save", "list_empty"}, + {"listdemo", "Save", "list_erased"}, + {"listdemo", "Mixed", "list_mixed"}, + {"listdemo", "Album", "list_shared"}, + {"listdemo", "Album", "list_before_pointer"}, + {"listdemo", "Sheet", "list_nested"}, + {"listdemo", "Army", "list_of_maps"}, + {"listdemo", "Unbounded", "list_migrates"}, + {"armdemo", "Holder", "arms_holder"}, + {"armdemo", "Hand", "arms_hand"}, + {"armdemo", "Chain", "arms_chain"}, + {"armdemo", "Gate", "arms_gate"}, + {"armdemo", "Nest", "arms_nest"}, + {"armdemo", "Ring", "arms_ring"}, + {"armdemo", "Rack", "arms_rack"}, + {"armdemo", "Tray", "arms_tray"}, + {"armdemo", "Gate", "arms_gate_text"}, + } + for _, c := range cases { + path := filepath.Join("testdata/wire/tables", c.name+".bin") + wire, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + roots = append(roots, &wireRoot{unit: c.unit, root: c.root, variable: true}) + seeds = append(seeds, &wireSeed{name: c.name, unit: c.unit, root: c.root, wire: wire, frame: frameWire(wire)}) + indexes = append(indexes, len(roots)-1) + } + actual, err := startWireLeg(driver, roots) + if err != nil { + t.Fatal(err) + } + defer actual.kill() + expected, err := startWireLeg(reference, roots) + if err != nil { + t.Fatal(err) + } + defer expected.kill() + for i := range roots { + if !actual.known[i] || !expected.known[i] { + t.Fatalf("codec absent for %s.%s", roots[i].unit, roots[i].root) + } + } + count := 0 + check := func(seed *wireSeed, index int, pass string, wire []byte) { + t.Helper() + if err := actual.send(index, wire); err != nil { + t.Fatal(err) + } + a, err := actual.receive() + if err != nil { + t.Fatalf("C died: %v\n%s", err, actual.stderr.String()) + } + if err := expected.send(index, wire); err != nil { + t.Fatal(err) + } + e, err := expected.receive() + if err != nil { + t.Fatalf("C++ died: %v\n%s", err, expected.stderr.String()) + } + if a.loaded != e.loaded || a.measure != e.measure || a.report != e.report || (a.loaded && (a.saveFail != e.saveFail || !bytes.Equal(a.saved, e.saved))) { + dir := filepath.Join("build", "c-collections-failures") + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "failed.bin"), wire, 0644); err != nil { + t.Fatal(err) + } + _ = os.WriteFile(filepath.Join(dir, "c.bin"), a.saved, 0644) + _ = os.WriteFile(filepath.Join(dir, "cpp.bin"), e.saved, 0644) + t.Fatalf("after %d mutants: %s %s\nC: loaded=%t measure=%d report=%s save-failed=%t\nC++: loaded=%t measure=%d report=%s save-failed=%t\nwire=%x", count, seed.name, pass, a.loaded, a.measure, a.report, a.saveFail, e.loaded, e.measure, e.report, e.saveFail, wire) + } + count++ + } + for i, s := range seeds { + enumerated(s, func(pass string, wire []byte) { check(s, indexes[i], pass, wire) }) + } + for i := range 20000 { + m := randomMutant(seeds, 1, i) + index := 0 + for j, s := range seeds { + if s == m.seed { + index = j + break + } + } + check(m.seed, index, m.pass, m.data) + } + t.Logf("C/C++ collections %s: %d mutants, identical reports, output bytes, and measured sizes", mode, count) +} diff --git a/test/conformance/harness/wirefuzzsize_test.go b/test/conformance/harness/wirefuzzsize_test.go new file mode 100644 index 000000000..ddf43e8fb --- /dev/null +++ b/test/conformance/harness/wirefuzzsize_test.go @@ -0,0 +1,27 @@ +package main + +import ( + "encoding/hex" + "testing" +) + +// The file-form size oracle must count raw blob bodies as well as table +// records. C++ CatalogLoadMeasure independently answers 400 for this vector. +func TestFileWireSizeIncludesBlobRecords(t *testing.T) { + _, _, units := corpus(t) + root, err := newWireRoot(units, "blobdemo", "Catalog", false, false) + if err != nil { + t.Fatal(err) + } + data, err := hex.DecodeString("010c0c03617274021102031106041107050c43060618010c05627269636b0708070000000811030911040a1105000b030102030c036361700608010c047461696c000b05deadbeef7f0c0b68656c6c6f20626c6f627300861b638ebaadbcc4030c9a5fcc128f0a03b2f40f72193b61dd7c58d1bafbf83bfffffffffffffffffb645ffecc6d4e8e43326721d7969cef054aa33067555b85a5024c86eb28aa5d28f025a0ba6c31e5e44f1c4f47c02e2f58fcaffad8e04b700c00000000000000") + if err != nil { + t.Fatal(err) + } + got, err := root.oracle(data) + if err != nil { + t.Fatal(err) + } + if !got.exact || got.bytes != 400 { + t.Fatalf("region size = %d (exact %v), want exactly 400", got.bytes, got.exact) + } +} diff --git a/testdata/golden/c/ExampleView.c b/testdata/golden/c/ExampleView.c new file mode 100644 index 000000000..1a4b6f3f9 --- /dev/null +++ b/testdata/golden/c/ExampleView.c @@ -0,0 +1,934 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "ExampleView.h" +static const TableFieldInfo schema_example_arm_align_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmAlign, lead ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmAlign, flag ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "s", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 4, (uint32_t) offsetof( ArmAlign, s ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->s ), (uint32_t) offsetof( ArmAlign, s_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(10)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmAlign, b ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1023.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmAlign, tail ), (uint32_t) sizeof( ( (ArmAlign *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arm_align_info_ = { "ArmAlign", (uint32_t) sizeof( ArmAlign ), 5, schema_example_arm_align_fields_, schema_example_arm_align_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arm_array_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmArray, lead ), (uint32_t) sizeof( ( (ArmArray *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmArray, flag ), (uint32_t) sizeof( ( (ArmArray *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "uint16", 0x0000000000000000ull, 7, 1, 1, 0, 3, (uint32_t) offsetof( ArmArray, items ), (uint32_t) sizeof( ( (ArmArray *) 0 )->items[0] ), (uint32_t) offsetof( ArmArray, items_count ), 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmArray, b ), (uint32_t) sizeof( ( (ArmArray *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmArray, tail ), (uint32_t) sizeof( ( (ArmArray *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arm_array_info_ = { "ArmArray", (uint32_t) sizeof( ArmArray ), 5, schema_example_arm_array_fields_, schema_example_arm_array_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arm_empty_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmEmpty, lead ), (uint32_t) sizeof( ( (ArmEmpty *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmEmpty, flag ), (uint32_t) sizeof( ( (ArmEmpty *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "a", NULL, "bits(19)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ArmEmpty, a ), (uint32_t) sizeof( ( (ArmEmpty *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 524287.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmEmpty, tail ), (uint32_t) sizeof( ( (ArmEmpty *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arm_empty_info_ = { "ArmEmpty", (uint32_t) sizeof( ArmEmpty ), 4, schema_example_arm_empty_fields_, schema_example_arm_empty_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arms_agree_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, lead ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, flag ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "a", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, a ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, b ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsAgree, tail ), (uint32_t) sizeof( ( (ArmsAgree *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arms_agree_info_ = { "ArmsAgree", (uint32_t) sizeof( ArmsAgree ), 5, schema_example_arms_agree_fields_, schema_example_arms_agree_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arms_disagree_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, lead ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, flag ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "a", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, a ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "flag", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, b ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!flag", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsDisagree, tail ), (uint32_t) sizeof( ( (ArmsDisagree *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arms_disagree_info_ = { "ArmsDisagree", (uint32_t) sizeof( ArmsDisagree ), 5, schema_example_arms_disagree_fields_, schema_example_arms_disagree_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arms_nested_fields_[] = { + { "lead", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, lead ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "outer", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, outer ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->outer ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "inner", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, inner ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->inner ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "outer", TableDocNone, 0, NULL, 0, NULL }, + { "x", NULL, "bits(29)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, x ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 5.36870911e+08, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "outer && inner", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, y ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "outer && !inner", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "bits(13)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, z ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!outer", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(6)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArmsNested, tail ), (uint32_t) sizeof( ( (ArmsNested *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 63.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arms_nested_info_ = { "ArmsNested", (uint32_t) sizeof( ArmsNested ), 7, schema_example_arms_nested_fields_, schema_example_arms_nested_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arr_eleven_fields_[] = { + { "items", NULL, "Eleven", 0x0000000000000000ull, 13, 1, 0, 0, 9, (uint32_t) offsetof( ArrEleven, items ), (uint32_t) sizeof( ( (ArrEleven *) 0 )->items[0] ), 0xffffffffu, 0xffffffffu, &schema_example_eleven_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arr_eleven_info_ = { "ArrEleven", (uint32_t) sizeof( ArrEleven ), 1, schema_example_arr_eleven_fields_, schema_example_arr_eleven_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arr_nested_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArrNested, lead ), (uint32_t) sizeof( ( (ArrNested *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "Eleven", 0x0000000000000000ull, 13, 1, 1, 0, 4, (uint32_t) offsetof( ArrNested, items ), (uint32_t) sizeof( ( (ArrNested *) 0 )->items[0] ), (uint32_t) offsetof( ArrNested, items_count ), 0xffffffffu, &schema_example_eleven_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArrNested, tail ), (uint32_t) sizeof( ( (ArrNested *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arr_nested_info_ = { "ArrNested", (uint32_t) sizeof( ArrNested ), 3, schema_example_arr_nested_fields_, schema_example_arr_nested_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_arr_tri3_fields_[] = { + { "items", NULL, "Tri3", 0x0000000000000000ull, 13, 1, 1, 0, 10, (uint32_t) offsetof( ArrTri3, items ), (uint32_t) sizeof( ( (ArrTri3 *) 0 )->items[0] ), (uint32_t) offsetof( ArrTri3, items_count ), 0xffffffffu, &schema_example_tri3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arr_tri3_info_ = { "ArrTri3", (uint32_t) sizeof( ArrTri3 ), 1, schema_example_arr_tri3_fields_, schema_example_arr_tri3_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_arr_uneven_items_outside_variants_[] = { + { "None", 0 }, + { "narrow", 0x0000000000000000ull }, + { "wide", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_arr_uneven_items_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( Uneven, as.narrow ), &schema_example_narrow_info_, NULL, (uint32_t)sizeof( ((Uneven *)0)->as.narrow ) }, + { (uint32_t)offsetof( Uneven, as.wide ), &schema_example_wide_info_, NULL, (uint32_t)sizeof( ((Uneven *)0)->as.wide ) }, +}; +static const TableUnionInfo schema_example_arr_uneven_items_outside_union_ = { (uint32_t) offsetof( Uneven, type ), (uint32_t) sizeof( ( (Uneven *) 0 )->type ), schema_example_arr_uneven_items_outside_arms_ }; +static const TableFieldInfo schema_example_arr_uneven_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArrUneven, lead ), (uint32_t) sizeof( ( (ArrUneven *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "Uneven", 0x0000000000000000ull, 15, 1, 1, 0, 3, (uint32_t) offsetof( ArrUneven, items ), (uint32_t) sizeof( ( (ArrUneven *) 0 )->items[0] ), (uint32_t) offsetof( ArrUneven, items_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_arr_uneven_items_outside_variants_, 1, NULL, NULL, -1, &schema_example_arr_uneven_items_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ArrUneven, tail ), (uint32_t) sizeof( ( (ArrUneven *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_arr_uneven_info_ = { "ArrUneven", (uint32_t) sizeof( ArrUneven ), 3, schema_example_arr_uneven_fields_, schema_example_arr_uneven_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_block_fields_[] = { + { "data", NULL, "bytes", 0x0000000000000000ull, 6, 1, 1, 0, 2000, (uint32_t) offsetof( Block, data ), (uint32_t) sizeof( ( (Block *) 0 )->data[0] ), (uint32_t) offsetof( Block, data_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_block_info_ = { "Block", (uint32_t) sizeof( Block ), 1, schema_example_block_fields_, schema_example_block_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_chat_fields_[] = { + { "text", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 256, (uint32_t) offsetof( Chat, text ), (uint32_t) sizeof( ( (Chat *) 0 )->text ), (uint32_t) offsetof( Chat, text_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_chat_info_ = { "Chat", (uint32_t) sizeof( Chat ), 1, schema_example_chat_fields_, schema_example_chat_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_compressed_probe_fields_[] = { + { "boundary", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( CompressedProbe, boundary ), (uint32_t) sizeof( ( (CompressedProbe *) 0 )->boundary ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "offset", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( CompressedProbe, offset ), (uint32_t) sizeof( ( (CompressedProbe *) 0 )->offset ), 0xffffffffu, 0xffffffffu, NULL, 1, -5.0, 5.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_compressed_probe_info_ = { "CompressedProbe", (uint32_t) sizeof( CompressedProbe ), 2, schema_example_compressed_probe_fields_, schema_example_compressed_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_default_arm_fields_[] = { + { "entries", NULL, "ProbeConfig", 0x0000000000000000ull, 13, 1, 1, 0, 2, (uint32_t) offsetof( DefaultArm, entries ), (uint32_t) sizeof( ( (DefaultArm *) 0 )->entries[0] ), (uint32_t) offsetof( DefaultArm, entries_count ), 0xffffffffu, &schema_example_probe_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "marker", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( DefaultArm, marker ), (uint32_t) sizeof( ( (DefaultArm *) 0 )->marker ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_default_arm_info_ = { "DefaultArm", (uint32_t) sizeof( DefaultArm ), 2, schema_example_default_arm_fields_, schema_example_default_arm_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_default_bulk_arm_fields_[] = { + { "payload", NULL, "DefaultArm", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( DefaultBulkArm, payload ), (uint32_t) sizeof( ( (DefaultBulkArm *) 0 )->payload ), 0xffffffffu, 0xffffffffu, &schema_example_default_arm_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "data", NULL, "bytes", 0x0000000000000000ull, 6, 1, 1, 0, 2, (uint32_t) offsetof( DefaultBulkArm, data ), (uint32_t) sizeof( ( (DefaultBulkArm *) 0 )->data[0] ), (uint32_t) offsetof( DefaultBulkArm, data_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_default_bulk_arm_info_ = { "DefaultBulkArm", (uint32_t) sizeof( DefaultBulkArm ), 2, schema_example_default_bulk_arm_fields_, schema_example_default_bulk_arm_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_default_choice_packet_choice_outside_variants_[] = { + { "None", 0 }, + { "first", 0x0000000000000000ull }, + { "second", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_default_choice_packet_choice_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( DefaultChoice, as.first ), &schema_example_default_arm_info_, NULL, (uint32_t)sizeof( ((DefaultChoice *)0)->as.first ) }, + { (uint32_t)offsetof( DefaultChoice, as.second ), &schema_example_default_arm_info_, NULL, (uint32_t)sizeof( ((DefaultChoice *)0)->as.second ) }, +}; +static const TableUnionInfo schema_example_default_choice_packet_choice_outside_union_ = { (uint32_t) offsetof( DefaultChoice, type ), (uint32_t) sizeof( ( (DefaultChoice *) 0 )->type ), schema_example_default_choice_packet_choice_outside_arms_ }; +static const TableFieldInfo schema_example_default_choice_packet_fields_[] = { + { "choice", NULL, "DefaultChoice", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( DefaultChoicePacket, choice ), (uint32_t) sizeof( ( (DefaultChoicePacket *) 0 )->choice ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_default_choice_packet_choice_outside_variants_, 1, NULL, NULL, -1, &schema_example_default_choice_packet_choice_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_default_choice_packet_info_ = { "DefaultChoicePacket", (uint32_t) sizeof( DefaultChoicePacket ), 1, schema_example_default_choice_packet_fields_, schema_example_default_choice_packet_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_eleven_fields_[] = { + { "a", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Eleven, a ), (uint32_t) sizeof( ( (Eleven *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Eleven, b ), (uint32_t) sizeof( ( (Eleven *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_eleven_info_ = { "Eleven", (uint32_t) sizeof( Eleven ), 2, schema_example_eleven_fields_, schema_example_eleven_reset_raw_, TableDocNone, 0, NULL }; + +const TableTypeInfo schema_example_empty_a_info_ = { "EmptyA", (uint32_t) sizeof( EmptyA ), 0, NULL, schema_example_empty_a_reset_raw_, TableDocNone, 0, NULL }; + +const TableTypeInfo schema_example_empty_b_info_ = { "EmptyB", (uint32_t) sizeof( EmptyB ), 0, NULL, schema_example_empty_b_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_expression_probe_fields_[] = { + { "hardpoint_index", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ExpressionProbe, hardpoint_index ), (uint32_t) sizeof( ( (ExpressionProbe *) 0 )->hardpoint_index ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "spin_rate", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ExpressionProbe, spin_rate ), (uint32_t) sizeof( ( (ExpressionProbe *) 0 )->spin_rate ), 0xffffffffu, 0xffffffffu, NULL, 1, 1024.0, 2048.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_expression_probe_info_ = { "ExpressionProbe", (uint32_t) sizeof( ExpressionProbe ), 2, schema_example_expression_probe_fields_, schema_example_expression_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_extreme_probe_fields_[] = { + { "floor_bound", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, floor_bound ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->floor_bound ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "doubled_floor", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, doubled_floor ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->doubled_floor ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ceiling_range", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, ceiling_range ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->ceiling_range ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "floor_default", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, floor_default ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->floor_default ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ceiling_default", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeProbe, ceiling_default ), (uint32_t) sizeof( ( (ExtremeProbe *) 0 )->ceiling_default ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_extreme_probe_info_ = { "ExtremeProbe", (uint32_t) sizeof( ExtremeProbe ), 5, schema_example_extreme_probe_fields_, schema_example_extreme_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_extreme_row_fields_[] = { + { "clamped_floor", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeRow, clamped_floor ), (uint32_t) sizeof( ( (ExtremeRow *) 0 )->clamped_floor ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "clamped_ceiling", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeRow, clamped_ceiling ), (uint32_t) sizeof( ( (ExtremeRow *) 0 )->clamped_ceiling ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "floor_def", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeRow, floor_def ), (uint32_t) sizeof( ( (ExtremeRow *) 0 )->floor_def ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ceiling_def", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ExtremeRow, ceiling_def ), (uint32_t) sizeof( ( (ExtremeRow *) 0 )->ceiling_def ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_extreme_row_info_ = { "ExtremeRow", (uint32_t) sizeof( ExtremeRow ), 4, schema_example_extreme_row_fields_, schema_example_extreme_row_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_f13_fields_[] = { + { "items", NULL, "uint16", 0x0000000000000000ull, 7, 1, 0, 0, 7, (uint32_t) offsetof( F13, items ), (uint32_t) sizeof( ( (F13 *) 0 )->items[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_f13_info_ = { "F13", (uint32_t) sizeof( F13 ), 1, schema_example_f13_fields_, schema_example_f13_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_handle_fields_[] = { + { "object_id", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( Handle, object_id ), (uint32_t) sizeof( ( (Handle *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 9999.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_sequence", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Handle, object_sequence ), (uint32_t) sizeof( ( (Handle *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_handle_info_ = { "Handle", (uint32_t) sizeof( Handle ), 2, schema_example_handle_fields_, schema_example_handle_reset_raw_, TableDocNone, 0, NULL }; + +const TableTypeInfo schema_example_heartbeat_info_ = { "Heartbeat", (uint32_t) sizeof( Heartbeat ), 0, NULL, schema_example_heartbeat_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_holds_empty_union_u_outside_variants_[] = { + { "None", 0 }, + { "a", 0x0000000000000000ull }, + { "b", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_holds_empty_union_u_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( EmptyUnion, as.a ), &schema_example_empty_a_info_, NULL, (uint32_t)sizeof( ((EmptyUnion *)0)->as.a ) }, + { (uint32_t)offsetof( EmptyUnion, as.b ), &schema_example_empty_b_info_, NULL, (uint32_t)sizeof( ((EmptyUnion *)0)->as.b ) }, +}; +static const TableUnionInfo schema_example_holds_empty_union_u_outside_union_ = { (uint32_t) offsetof( EmptyUnion, type ), (uint32_t) sizeof( ( (EmptyUnion *) 0 )->type ), schema_example_holds_empty_union_u_outside_arms_ }; +static const TableFieldInfo schema_example_holds_empty_union_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( HoldsEmptyUnion, lead ), (uint32_t) sizeof( ( (HoldsEmptyUnion *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u", NULL, "EmptyUnion", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( HoldsEmptyUnion, u ), (uint32_t) sizeof( ( (HoldsEmptyUnion *) 0 )->u ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_holds_empty_union_u_outside_variants_, 1, NULL, NULL, -1, &schema_example_holds_empty_union_u_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(7)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( HoldsEmptyUnion, tail ), (uint32_t) sizeof( ( (HoldsEmptyUnion *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_holds_empty_union_info_ = { "HoldsEmptyUnion", (uint32_t) sizeof( HoldsEmptyUnion ), 3, schema_example_holds_empty_union_fields_, schema_example_holds_empty_union_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_holds_uneven_u_outside_variants_[] = { + { "None", 0 }, + { "narrow", 0x0000000000000000ull }, + { "wide", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_holds_uneven_u_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( Uneven, as.narrow ), &schema_example_narrow_info_, NULL, (uint32_t)sizeof( ((Uneven *)0)->as.narrow ) }, + { (uint32_t)offsetof( Uneven, as.wide ), &schema_example_wide_info_, NULL, (uint32_t)sizeof( ((Uneven *)0)->as.wide ) }, +}; +static const TableUnionInfo schema_example_holds_uneven_u_outside_union_ = { (uint32_t) offsetof( Uneven, type ), (uint32_t) sizeof( ( (Uneven *) 0 )->type ), schema_example_holds_uneven_u_outside_arms_ }; +static const TableFieldInfo schema_example_holds_uneven_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( HoldsUneven, lead ), (uint32_t) sizeof( ( (HoldsUneven *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u", NULL, "Uneven", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( HoldsUneven, u ), (uint32_t) sizeof( ( (HoldsUneven *) 0 )->u ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_holds_uneven_u_outside_variants_, 1, NULL, NULL, -1, &schema_example_holds_uneven_u_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(11)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( HoldsUneven, tail ), (uint32_t) sizeof( ( (HoldsUneven *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2047.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_holds_uneven_info_ = { "HoldsUneven", (uint32_t) sizeof( HoldsUneven ), 3, schema_example_holds_uneven_fields_, schema_example_holds_uneven_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_input_fields_[] = { + { "stick_x", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, stick_x ), (uint32_t) sizeof( ( (Input *) 0 )->stick_x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "stick_y", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, stick_y ), (uint32_t) sizeof( ( (Input *) 0 )->stick_y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "throttle", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, throttle ), (uint32_t) sizeof( ( (Input *) 0 )->throttle ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "yaw", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, yaw ), (uint32_t) sizeof( ( (Input *) 0 )->yaw ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pitch", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Input, pitch ), (uint32_t) sizeof( ( (Input *) 0 )->pitch ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "fire", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, fire ), (uint32_t) sizeof( ( (Input *) 0 )->fire ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "alt_fire", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, alt_fire ), (uint32_t) sizeof( ( (Input *) 0 )->alt_fire ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "boost", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, boost ), (uint32_t) sizeof( ( (Input *) 0 )->boost ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "brake", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, brake ), (uint32_t) sizeof( ( (Input *) 0 )->brake ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "aim", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, aim ), (uint32_t) sizeof( ( (Input *) 0 )->aim ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "lock_on", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, lock_on ), (uint32_t) sizeof( ( (Input *) 0 )->lock_on ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "zoom", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, zoom ), (uint32_t) sizeof( ( (Input *) 0 )->zoom ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ping", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Input, ping ), (uint32_t) sizeof( ( (Input *) 0 )->ping ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_input_info_ = { "Input", (uint32_t) sizeof( Input ), 13, schema_example_input_fields_, schema_example_input_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_input_packet_fields_[] = { + { "synchronize_sequence", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( InputPacket, synchronize_sequence ), (uint32_t) sizeof( ( (InputPacket *) 0 )->synchronize_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "current_frame", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( InputPacket, current_frame ), (uint32_t) sizeof( ( (InputPacket *) 0 )->current_frame ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "start_frame", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( InputPacket, start_frame ), (uint32_t) sizeof( ( (InputPacket *) 0 )->start_frame ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "inputs", NULL, "Input", 0x0000000000000000ull, 13, 1, 1, 0, 16, (uint32_t) offsetof( InputPacket, inputs ), (uint32_t) sizeof( ( (InputPacket *) 0 )->inputs[0] ), (uint32_t) offsetof( InputPacket, inputs_count ), 0xffffffffu, &schema_example_input_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_input_packet_info_ = { "InputPacket", (uint32_t) sizeof( InputPacket ), 4, schema_example_input_packet_fields_, schema_example_input_packet_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_narrow_fields_[] = { + { "n", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Narrow, n ), (uint32_t) sizeof( ( (Narrow *) 0 )->n ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_narrow_info_ = { "Narrow", (uint32_t) sizeof( Narrow ), 1, schema_example_narrow_fields_, schema_example_narrow_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_array_fields_[] = { + { "samples", NULL, "ProbeSample", 0x0000000000000000ull, 13, 1, 0, 0, 2, (uint32_t) offsetof( ProbeArray, samples ), (uint32_t) sizeof( ( (ProbeArray *) 0 )->samples[0] ), 0xffffffffu, 0xffffffffu, &schema_example_probe_sample_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "config", NULL, "ProbeConfig", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ProbeArray, config ), (uint32_t) sizeof( ( (ProbeArray *) 0 )->config ), 0xffffffffu, 0xffffffffu, &schema_example_probe_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_array_info_ = { "ProbeArray", (uint32_t) sizeof( ProbeArray ), 2, schema_example_probe_array_fields_, schema_example_probe_array_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_bits_fields_[] = { + { "small", NULL, "bits(9)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, small ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->small ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 511.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "boundary", NULL, "bits(33)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, boundary ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->boundary ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8.589934591e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "wide", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, wide ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->wide ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "sensor", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, sensor ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->sensor ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "nonce", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeBits, nonce ), (uint32_t) sizeof( ( (ProbeBits *) 0 )->nonce ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_bits_info_ = { "ProbeBits", (uint32_t) sizeof( ProbeBits ), 5, schema_example_probe_bits_fields_, schema_example_probe_bits_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_probe_collider_shape_outside_variants_[] = { + { "None", 0 }, + { "ring", 0x0000000000000000ull }, + { "slab", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_probe_collider_shape_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( ProbeShape, as.ring ), &schema_example_probe_ring_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.ring ) }, + { (uint32_t)offsetof( ProbeShape, as.slab ), &schema_example_probe_slab_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.slab ) }, +}; +static const TableUnionInfo schema_example_probe_collider_shape_outside_union_ = { (uint32_t) offsetof( ProbeShape, type ), (uint32_t) sizeof( ( (ProbeShape *) 0 )->type ), schema_example_probe_collider_shape_outside_arms_ }; +static const TableVariantInfo schema_example_probe_collider_backup_outside_variants_[] = { + { "None", 0 }, + { "ring", 0x0000000000000000ull }, + { "slab", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_probe_collider_backup_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( ProbeShape, as.ring ), &schema_example_probe_ring_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.ring ) }, + { (uint32_t)offsetof( ProbeShape, as.slab ), &schema_example_probe_slab_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.slab ) }, +}; +static const TableUnionInfo schema_example_probe_collider_backup_outside_union_ = { (uint32_t) offsetof( ProbeShape, type ), (uint32_t) sizeof( ( (ProbeShape *) 0 )->type ), schema_example_probe_collider_backup_outside_arms_ }; +static const TableVariantInfo schema_example_probe_collider_extras_outside_variants_[] = { + { "None", 0 }, + { "ring", 0x0000000000000000ull }, + { "slab", 0x0000000000000000ull }, +}; +static const TableUnionArmInfo schema_example_probe_collider_extras_outside_arms_[] = { + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( ProbeShape, as.ring ), &schema_example_probe_ring_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.ring ) }, + { (uint32_t)offsetof( ProbeShape, as.slab ), &schema_example_probe_slab_info_, NULL, (uint32_t)sizeof( ((ProbeShape *)0)->as.slab ) }, +}; +static const TableUnionInfo schema_example_probe_collider_extras_outside_union_ = { (uint32_t) offsetof( ProbeShape, type ), (uint32_t) sizeof( ( (ProbeShape *) 0 )->type ), schema_example_probe_collider_extras_outside_arms_ }; +static const TableFieldInfo schema_example_probe_collider_fields_[] = { + { "armor", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProbeCollider, armor ), (uint32_t) sizeof( ( (ProbeCollider *) 0 )->armor ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "shape", NULL, "ProbeShape", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( ProbeCollider, shape ), (uint32_t) sizeof( ( (ProbeCollider *) 0 )->shape ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_probe_collider_shape_outside_variants_, 1, NULL, NULL, -1, &schema_example_probe_collider_shape_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "backup", NULL, "ProbeShape", 0x0000000000000000ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( ProbeCollider, backup ), (uint32_t) sizeof( ( (ProbeCollider *) 0 )->backup ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_probe_collider_backup_outside_variants_, 1, NULL, NULL, -1, &schema_example_probe_collider_backup_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, + { "extras", NULL, "ProbeShape", 0x0000000000000000ull, 15, 1, 1, 0, 2, (uint32_t) offsetof( ProbeCollider, extras ), (uint32_t) sizeof( ( (ProbeCollider *) 0 )->extras[0] ), (uint32_t) offsetof( ProbeCollider, extras_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_probe_collider_extras_outside_variants_, 1, NULL, NULL, -1, &schema_example_probe_collider_extras_outside_union_, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_collider_info_ = { "ProbeCollider", (uint32_t) sizeof( ProbeCollider ), 4, schema_example_probe_collider_fields_, schema_example_probe_collider_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_probe_config_preferred_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Laser", 0x0000000000000000ull }, + { "Missile", 0x0000000000000000ull }, + { "Railgun", 0x0000000000000000ull }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, +}; +static const TableFieldInfo schema_example_probe_config_fields_[] = { + { "retries", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ProbeConfig, retries ), (uint32_t) sizeof( ( (ProbeConfig *) 0 )->retries ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "preferred", NULL, "Weapon", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ProbeConfig, preferred ), (uint32_t) sizeof( ( (ProbeConfig *) 0 )->preferred ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 15, schema_example_probe_config_preferred_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_config_info_ = { "ProbeConfig", (uint32_t) sizeof( ProbeConfig ), 2, schema_example_probe_config_fields_, schema_example_probe_config_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_header_fields_[] = { + { "version", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProbeHeader, version ), (uint32_t) sizeof( ( (ProbeHeader *) 0 )->version ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "probe_id", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeHeader, probe_id ), (uint32_t) sizeof( ( (ProbeHeader *) 0 )->probe_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_header_info_ = { "ProbeHeader", (uint32_t) sizeof( ProbeHeader ), 2, schema_example_probe_header_fields_, schema_example_probe_header_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_probe_report_flags_outside_variants_[] = { + { "Armed", 0 }, + { "Cloaked", 0 }, + { "Damaged", 0 }, +}; +static const TableFieldInfo schema_example_probe_report_fields_[] = { + { "header", NULL, "ProbeHeader", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ProbeReport, header ), (uint32_t) sizeof( ( (ProbeReport *) 0 )->header ), 0xffffffffu, 0xffffffffu, &schema_example_probe_header_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", NULL, "ProbeFlags", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProbeReport, flags ), (uint32_t) sizeof( ( (ProbeReport *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_probe_report_flags_outside_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "echo", NULL, "Test", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ProbeReport, echo ), (uint32_t) sizeof( ( (ProbeReport *) 0 )->echo ), 0xffffffffu, 0xffffffffu, &schema_example_test_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_report_info_ = { "ProbeReport", (uint32_t) sizeof( ProbeReport ), 3, schema_example_probe_report_fields_, schema_example_probe_report_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_ring_fields_[] = { + { "radius", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ProbeRing, radius ), (uint32_t) sizeof( ( (ProbeRing *) 0 )->radius ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_ring_info_ = { "ProbeRing", (uint32_t) sizeof( ProbeRing ), 1, schema_example_probe_ring_fields_, schema_example_probe_ring_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_probe_sample_weapon_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Laser", 0x0000000000000000ull }, + { "Missile", 0x0000000000000000ull }, + { "Railgun", 0x0000000000000000ull }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, +}; +static const TableFieldInfo schema_example_probe_sample_fields_[] = { + { "active", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, active ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->active ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "orientation", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, orientation ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->orientation ), 0xffffffffu, 0xffffffffu, NULL, 1, -180.0, 180.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "raw_delta", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, raw_delta ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->raw_delta ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "big_delta", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, big_delta ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->big_delta ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "weapon", NULL, "Weapon", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, weapon ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->weapon ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 15, schema_example_probe_sample_weapon_outside_variants_, 1, NULL, NULL, -1, NULL, "active", TableDocNone, 0, NULL, 0, NULL }, + { "has_target", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, has_target ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->has_target ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "active", TableDocNone, 0, NULL, 0, NULL }, + { "target_id", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, target_id ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "active && has_target", TableDocNone, 0, NULL, 0, NULL }, + { "idle_ticks", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSample, idle_ticks ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->idle_ticks ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!active", TableDocNone, 0, NULL, 0, NULL }, + { "samples", NULL, "uint16", 0x0000000000000000ull, 7, 1, 1, 0, 8, (uint32_t) offsetof( ProbeSample, samples ), (uint32_t) sizeof( ( (ProbeSample *) 0 )->samples[0] ), (uint32_t) offsetof( ProbeSample, samples_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_sample_info_ = { "ProbeSample", (uint32_t) sizeof( ProbeSample ), 9, schema_example_probe_sample_fields_, schema_example_probe_sample_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_probe_slab_fields_[] = { + { "width", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSlab, width ), (uint32_t) sizeof( ( (ProbeSlab *) 0 )->width ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "height", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProbeSlab, height ), (uint32_t) sizeof( ( (ProbeSlab *) 0 )->height ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_probe_slab_info_ = { "ProbeSlab", (uint32_t) sizeof( ProbeSlab ), 2, schema_example_probe_slab_fields_, schema_example_probe_slab_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_quantized_position_fields_[] = { + { "x", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedPosition, x ), (uint32_t) sizeof( ( (QuantizedPosition *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -8.388608e+06, 8.388608e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedPosition, y ), (uint32_t) sizeof( ( (QuantizedPosition *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -8.388608e+06, 8.388608e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedPosition, z ), (uint32_t) sizeof( ( (QuantizedPosition *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -8.388608e+06, 8.388608e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_quantized_position_info_ = { "QuantizedPosition", (uint32_t) sizeof( QuantizedPosition ), 3, schema_example_quantized_position_fields_, schema_example_quantized_position_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_quantized_rotation_fields_[] = { + { "x", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedRotation, x ), (uint32_t) sizeof( ( (QuantizedRotation *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -1024.0, 1024.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedRotation, y ), (uint32_t) sizeof( ( (QuantizedRotation *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -1024.0, 1024.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedRotation, z ), (uint32_t) sizeof( ( (QuantizedRotation *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -1024.0, 1024.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "w", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedRotation, w ), (uint32_t) sizeof( ( (QuantizedRotation *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 1, -1024.0, 1024.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_quantized_rotation_info_ = { "QuantizedRotation", (uint32_t) sizeof( QuantizedRotation ), 4, schema_example_quantized_rotation_fields_, schema_example_quantized_rotation_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_quantized_velocity_fields_[] = { + { "x", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedVelocity, x ), (uint32_t) sizeof( ( (QuantizedVelocity *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.097152e+06, 2.097152e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedVelocity, y ), (uint32_t) sizeof( ( (QuantizedVelocity *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.097152e+06, 2.097152e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( QuantizedVelocity, z ), (uint32_t) sizeof( ( (QuantizedVelocity *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.097152e+06, 2.097152e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_quantized_velocity_info_ = { "QuantizedVelocity", (uint32_t) sizeof( QuantizedVelocity ), 3, schema_example_quantized_velocity_fields_, schema_example_quantized_velocity_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_quat_fields_[] = { + { "x", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Quat, x ), (uint32_t) sizeof( ( (Quat *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Quat, y ), (uint32_t) sizeof( ( (Quat *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Quat, z ), (uint32_t) sizeof( ( (Quat *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "w", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Quat, w ), (uint32_t) sizeof( ( (Quat *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_quat_info_ = { "Quat", (uint32_t) sizeof( Quat ), 4, schema_example_quat_fields_, schema_example_quat_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_regain_after_align_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, lead ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "uint16", 0x0000000000000000ull, 7, 1, 1, 0, 3, (uint32_t) offsetof( RegainAfterAlign, items ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->items[0] ), (uint32_t) offsetof( RegainAfterAlign, items_count ), 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "s", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 4, (uint32_t) offsetof( RegainAfterAlign, s ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->s ), (uint32_t) offsetof( RegainAfterAlign, s_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "p", NULL, "bits(32)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, p ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->p ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "q", NULL, "bits(29)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, q ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->q ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 5.36870911e+08, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "r", NULL, "bits(19)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, r ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->r ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 524287.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(4)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RegainAfterAlign, tail ), (uint32_t) sizeof( ( (RegainAfterAlign *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 15.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_regain_after_align_info_ = { "RegainAfterAlign", (uint32_t) sizeof( RegainAfterAlign ), 7, schema_example_regain_after_align_fields_, schema_example_regain_after_align_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_render_block_fields_[] = { + { "worker_index", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderBlock, worker_index ), (uint32_t) sizeof( ( (RenderBlock *) 0 )->worker_index ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "sprite_count_hint", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderBlock, sprite_count_hint ), (uint32_t) sizeof( ( (RenderBlock *) 0 )->sprite_count_hint ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "sprites", NULL, "RenderSprite", 0x0000000000000000ull, 13, 1, 1, 0, 64, (uint32_t) offsetof( RenderBlock, sprites ), (uint32_t) sizeof( ( (RenderBlock *) 0 )->sprites[0] ), (uint32_t) offsetof( RenderBlock, sprites_count ), 0xffffffffu, &schema_example_render_sprite_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_render_block_info_ = { "RenderBlock", (uint32_t) sizeof( RenderBlock ), 3, schema_example_render_block_fields_, schema_example_render_block_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_render_sprite_team_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Red", 0x0000000000000000ull }, + { "Blue", 0x0000000000000000ull }, +}; +static const TableFieldInfo schema_example_render_sprite_fields_[] = { + { "sort_key", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, sort_key ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->sort_key ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "mesh_id", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, mesh_id ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->mesh_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "material_id", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, material_id ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->material_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "layer", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, layer ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->layer ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", NULL, "Team", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderSprite, team ), (uint32_t) sizeof( ( (RenderSprite *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_render_sprite_team_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_render_sprite_info_ = { "RenderSprite", (uint32_t) sizeof( RenderSprite ), 5, schema_example_render_sprite_fields_, schema_example_render_sprite_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_rigid_body_fields_[] = { + { "position", NULL, "Vec3", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, position ), (uint32_t) sizeof( ( (RigidBody *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_example_vec3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "orientation", NULL, "Quat", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, orientation ), (uint32_t) sizeof( ( (RigidBody *) 0 )->orientation ), 0xffffffffu, 0xffffffffu, &schema_example_quat_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "at_rest", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, at_rest ), (uint32_t) sizeof( ( (RigidBody *) 0 )->at_rest ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "linear_velocity", NULL, "Vec3", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, linear_velocity ), (uint32_t) sizeof( ( (RigidBody *) 0 )->linear_velocity ), 0xffffffffu, 0xffffffffu, &schema_example_vec3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!at_rest", TableDocNone, 0, NULL, 0, NULL }, + { "angular_velocity", NULL, "Vec3", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RigidBody, angular_velocity ), (uint32_t) sizeof( ( (RigidBody *) 0 )->angular_velocity ), 0xffffffffu, 0xffffffffu, &schema_example_vec3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!at_rest", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_rigid_body_info_ = { "RigidBody", (uint32_t) sizeof( RigidBody ), 5, schema_example_rigid_body_fields_, schema_example_rigid_body_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_example_ship_create_ship_type_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Fighter", 0x0000000000000000ull }, + { "Corvette", 0x0000000000000000ull }, + { "Bomber", 0x0000000000000000ull }, + { "Destroyer", 0x0000000000000000ull }, + { "Carrier", 0x0000000000000000ull }, +}; +static const TableVariantInfo schema_example_ship_create_flags_outside_variants_[] = { + { "FiringLaser", 0 }, + { "Boosting", 0 }, + { "Braking", 0 }, + { "Aiming", 0 }, +}; +static const TableVariantInfo schema_example_ship_create_team_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Red", 0x0000000000000000ull }, + { "Blue", 0x0000000000000000ull }, +}; +static const TableVariantInfo schema_example_ship_create_pending_outside_variants_[] = { + { "None", 0x0000000000000000ull }, +}; +static const TableFieldInfo schema_example_ship_create_fields_[] = { + { "ship_type", NULL, "ShipType", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, ship_type ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->ship_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 5, schema_example_ship_create_ship_type_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "position", NULL, "QuantizedPosition", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, position ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_example_quantized_position_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", NULL, "QuantizedRotation", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, rotation ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_example_quantized_rotation_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "linear_velocity", NULL, "QuantizedVelocity", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, linear_velocity ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->linear_velocity ), 0xffffffffu, 0xffffffffu, &schema_example_quantized_velocity_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_flags", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, has_flags ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->has_flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", NULL, "ShipFlags", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, flags ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_example_ship_create_flags_outside_variants_, 0, NULL, NULL, -1, NULL, "has_flags", TableDocNone, 0, NULL, 0, NULL }, + { "team", NULL, "Team", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, team ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_example_ship_create_team_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "health", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, health ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "thrust", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, thrust ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->thrust ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pending", NULL, "Pending", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( ShipCreate, pending ), (uint32_t) sizeof( ( (ShipCreate *) 0 )->pending ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 0, schema_example_ship_create_pending_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_ship_create_info_ = { "ShipCreate", (uint32_t) sizeof( ShipCreate ), 10, schema_example_ship_create_fields_, schema_example_ship_create_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_sole_fields_[] = { + { "only", NULL, "bits(13)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( Sole, only ), (uint32_t) sizeof( ( (Sole *) 0 )->only ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_sole_info_ = { "Sole", (uint32_t) sizeof( Sole ), 1, schema_example_sole_fields_, schema_example_sole_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_chunk_fields_[] = { + { "values", NULL, "uint16", 0x0000000000000000ull, 7, 1, 0, 0, 4, (uint32_t) offsetof( SpanChunk, values ), (uint32_t) sizeof( ( (SpanChunk *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_chunk_info_ = { "SpanChunk", (uint32_t) sizeof( SpanChunk ), 1, schema_example_span_chunk_fields_, schema_example_span_chunk_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_f64_fields_[] = { + { "values", NULL, "float64", 0x0000000000000000ull, 11, 1, 0, 0, 2, (uint32_t) offsetof( SpanF64, values ), (uint32_t) sizeof( ( (SpanF64 *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_f64_info_ = { "SpanF64", (uint32_t) sizeof( SpanF64 ), 1, schema_example_span_f64_fields_, schema_example_span_f64_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_i64_fields_[] = { + { "values", NULL, "int64", 0x0000000000000000ull, 5, 1, 0, 0, 2, (uint32_t) offsetof( SpanI64, values ), (uint32_t) sizeof( ( (SpanI64 *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_i64_info_ = { "SpanI64", (uint32_t) sizeof( SpanI64 ), 1, schema_example_span_i64_fields_, schema_example_span_i64_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_one_fields_[] = { + { "values", NULL, "uint64", 0x0000000000000000ull, 9, 1, 0, 0, 1, (uint32_t) offsetof( SpanOne, values ), (uint32_t) sizeof( ( (SpanOne *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_one_info_ = { "SpanOne", (uint32_t) sizeof( SpanOne ), 1, schema_example_span_one_fields_, schema_example_span_one_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_tail_fields_[] = { + { "values", NULL, "float64", 0x0000000000000000ull, 11, 1, 0, 0, 2, (uint32_t) offsetof( SpanTail, values ), (uint32_t) sizeof( ( (SpanTail *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( SpanTail, tail ), (uint32_t) sizeof( ( (SpanTail *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_tail_info_ = { "SpanTail", (uint32_t) sizeof( SpanTail ), 2, schema_example_span_tail_fields_, schema_example_span_tail_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_twice_fields_[] = { + { "a", NULL, "float64", 0x0000000000000000ull, 11, 1, 0, 0, 2, (uint32_t) offsetof( SpanTwice, a ), (uint32_t) sizeof( ( (SpanTwice *) 0 )->a[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "float64", 0x0000000000000000ull, 11, 1, 0, 0, 2, (uint32_t) offsetof( SpanTwice, b ), (uint32_t) sizeof( ( (SpanTwice *) 0 )->b[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_twice_info_ = { "SpanTwice", (uint32_t) sizeof( SpanTwice ), 2, schema_example_span_twice_fields_, schema_example_span_twice_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_span_u64_fields_[] = { + { "values", NULL, "uint64", 0x0000000000000000ull, 9, 1, 0, 0, 2, (uint32_t) offsetof( SpanU64, values ), (uint32_t) sizeof( ( (SpanU64 *) 0 )->values[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_span_u64_info_ = { "SpanU64", (uint32_t) sizeof( SpanU64 ), 1, schema_example_span_u64_fields_, schema_example_span_u64_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_strs_fields_[] = { + { "lead", NULL, "bits(5)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Strs, lead ), (uint32_t) sizeof( ( (Strs *) 0 )->lead ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 31.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "s", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 8, (uint32_t) offsetof( Strs, s ), (uint32_t) sizeof( ( (Strs *) 0 )->s ), (uint32_t) offsetof( Strs, s_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bytes", 0x0000000000000000ull, 6, 1, 1, 0, 8, (uint32_t) offsetof( Strs, b ), (uint32_t) sizeof( ( (Strs *) 0 )->b[0] ), (uint32_t) offsetof( Strs, b_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "bits(3)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Strs, tail ), (uint32_t) sizeof( ( (Strs *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_strs_info_ = { "Strs", (uint32_t) sizeof( Strs ), 4, schema_example_strs_fields_, schema_example_strs_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_test_fields_[] = { + { "test_a", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( Test, test_a ), (uint32_t) sizeof( ( (Test *) 0 )->test_a ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "test_b", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( Test, test_b ), (uint32_t) sizeof( ( (Test *) 0 )->test_b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "test_c", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( Test, test_c ), (uint32_t) sizeof( ( (Test *) 0 )->test_c ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "test_d", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( Test, test_d ), (uint32_t) sizeof( ( (Test *) 0 )->test_d ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_test_info_ = { "Test", (uint32_t) sizeof( Test ), 4, schema_example_test_fields_, schema_example_test_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_test_data_fields_[] = { + { "a", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TestData, a ), (uint32_t) sizeof( ( (TestData *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TestData, b ), (uint32_t) sizeof( ( (TestData *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "c", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TestData, c ), (uint32_t) sizeof( ( (TestData *) 0 )->c ), 0xffffffffu, 0xffffffffu, NULL, 1, -100.0, 150.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "d", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TestData, d ), (uint32_t) sizeof( ( (TestData *) 0 )->d ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "e", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TestData, e ), (uint32_t) sizeof( ( (TestData *) 0 )->e ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "f", NULL, "bits(8)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TestData, f ), (uint32_t) sizeof( ( (TestData *) 0 )->f ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "g", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( TestData, g ), (uint32_t) sizeof( ( (TestData *) 0 )->g ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "items", NULL, "int32", 0x0000000000000000ull, 4, 1, 1, 0, 16, (uint32_t) offsetof( TestData, items ), (uint32_t) sizeof( ( (TestData *) 0 )->items[0] ), (uint32_t) offsetof( TestData, items_count ), 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "float_value", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TestData, float_value ), (uint32_t) sizeof( ( (TestData *) 0 )->float_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "compressed_float_value", NULL, "float32", 0x0000000000000000ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TestData, compressed_float_value ), (uint32_t) sizeof( ( (TestData *) 0 )->compressed_float_value ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "double_value", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( TestData, double_value ), (uint32_t) sizeof( ( (TestData *) 0 )->double_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "int8_value", NULL, "int8", 0x0000000000000000ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( TestData, int8_value ), (uint32_t) sizeof( ( (TestData *) 0 )->int8_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "int16_value", NULL, "int16", 0x0000000000000000ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( TestData, int16_value ), (uint32_t) sizeof( ( (TestData *) 0 )->int16_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "uint8_value", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( TestData, uint8_value ), (uint32_t) sizeof( ( (TestData *) 0 )->uint8_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "uint16_value", NULL, "uint16", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TestData, uint16_value ), (uint32_t) sizeof( ( (TestData *) 0 )->uint16_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "uint32_value", NULL, "uint32", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( TestData, uint32_value ), (uint32_t) sizeof( ( (TestData *) 0 )->uint32_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "uint64_value", NULL, "uint64", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TestData, uint64_value ), (uint32_t) sizeof( ( (TestData *) 0 )->uint64_value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "int64_full", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( TestData, int64_full ), (uint32_t) sizeof( ( (TestData *) 0 )->int64_full ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "int64_range", NULL, "int64", 0x0000000000000000ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( TestData, int64_range ), (uint32_t) sizeof( ( (TestData *) 0 )->int64_range ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+12, 1e+12, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "fixed_bytes", NULL, "uint8", 0x0000000000000000ull, 6, 1, 0, 0, 17, (uint32_t) offsetof( TestData, fixed_bytes ), (uint32_t) sizeof( ( (TestData *) 0 )->fixed_bytes[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "text", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 255, (uint32_t) offsetof( TestData, text ), (uint32_t) sizeof( ( (TestData *) 0 )->text ), (uint32_t) offsetof( TestData, text_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_test_data_info_ = { "TestData", (uint32_t) sizeof( TestData ), 21, schema_example_test_data_fields_, schema_example_test_data_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_tri3_fields_[] = { + { "a", NULL, "bits(1)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Tri3, a ), (uint32_t) sizeof( ( (Tri3 *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(2)", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( Tri3, b ), (uint32_t) sizeof( ( (Tri3 *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 3.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_tri3_info_ = { "Tri3", (uint32_t) sizeof( Tri3 ), 2, schema_example_tri3_fields_, schema_example_tri3_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_trio_fields_[] = { + { "a", NULL, "bits(20)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( Trio, a ), (uint32_t) sizeof( ( (Trio *) 0 )->a ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.048575e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", NULL, "bits(20)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( Trio, b ), (uint32_t) sizeof( ( (Trio *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.048575e+06, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "c", NULL, "bits(24)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( Trio, c ), (uint32_t) sizeof( ( (Trio *) 0 )->c ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.6777215e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_trio_info_ = { "Trio", (uint32_t) sizeof( Trio ), 3, schema_example_trio_fields_, schema_example_trio_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_trio_first_fields_[] = { + { "inner", NULL, "Trio", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( TrioFirst, inner ), (uint32_t) sizeof( ( (TrioFirst *) 0 )->inner ), 0xffffffffu, 0xffffffffu, &schema_example_trio_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "trailer", NULL, "bits(16)", 0x0000000000000000ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( TrioFirst, trailer ), (uint32_t) sizeof( ( (TrioFirst *) 0 )->trailer ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_trio_first_info_ = { "TrioFirst", (uint32_t) sizeof( TrioFirst ), 2, schema_example_trio_first_fields_, schema_example_trio_first_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_trio_sole_fields_[] = { + { "inner", NULL, "Trio", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( TrioSole, inner ), (uint32_t) sizeof( ( (TrioSole *) 0 )->inner ), 0xffffffffu, 0xffffffffu, &schema_example_trio_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_trio_sole_info_ = { "TrioSole", (uint32_t) sizeof( TrioSole ), 1, schema_example_trio_sole_fields_, schema_example_trio_sole_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_trio_straddle_fields_[] = { + { "pad0", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad0 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad0 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad1", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad1 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad1 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad2", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad2 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad2 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad3", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad3 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad3 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad4", NULL, "bits(64)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad4 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad4 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pad5", NULL, "bits(24)", 0x0000000000000000ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, pad5 ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->pad5 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.6777215e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "inner", NULL, "Trio", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( TrioStraddle, inner ), (uint32_t) sizeof( ( (TrioStraddle *) 0 )->inner ), 0xffffffffu, 0xffffffffu, &schema_example_trio_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_trio_straddle_info_ = { "TrioStraddle", (uint32_t) sizeof( TrioStraddle ), 7, schema_example_trio_straddle_fields_, schema_example_trio_straddle_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_vec2_fields_[] = { + { "x", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec2, x ), (uint32_t) sizeof( ( (Vec2 *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec2, y ), (uint32_t) sizeof( ( (Vec2 *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_vec2_info_ = { "Vec2", (uint32_t) sizeof( Vec2 ), 2, schema_example_vec2_fields_, schema_example_vec2_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_vec3_fields_[] = { + { "x", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec3, x ), (uint32_t) sizeof( ( (Vec3 *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec3, y ), (uint32_t) sizeof( ( (Vec3 *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "float64", 0x0000000000000000ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( Vec3, z ), (uint32_t) sizeof( ( (Vec3 *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_vec3_info_ = { "Vec3", (uint32_t) sizeof( Vec3 ), 3, schema_example_vec3_fields_, schema_example_vec3_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w1_fields_[] = { + { "items", NULL, "uint8", 0x0000000000000000ull, 6, 1, 1, 0, 20, (uint32_t) offsetof( W1, items ), (uint32_t) sizeof( ( (W1 *) 0 )->items[0] ), (uint32_t) offsetof( W1, items_count ), 0xffffffffu, NULL, 1, 0.0, 1.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w1_info_ = { "W1", (uint32_t) sizeof( W1 ), 1, schema_example_w1_fields_, schema_example_w1_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w13_fields_[] = { + { "items", NULL, "uint16", 0x0000000000000000ull, 7, 1, 1, 0, 12, (uint32_t) offsetof( W13, items ), (uint32_t) sizeof( ( (W13 *) 0 )->items[0] ), (uint32_t) offsetof( W13, items_count ), 0xffffffffu, NULL, 1, 0.0, 8191.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w13_info_ = { "W13", (uint32_t) sizeof( W13 ), 1, schema_example_w13_fields_, schema_example_w13_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w17_fields_[] = { + { "items", NULL, "uint32", 0x0000000000000000ull, 8, 1, 1, 0, 9, (uint32_t) offsetof( W17, items ), (uint32_t) sizeof( ( (W17 *) 0 )->items[0] ), (uint32_t) offsetof( W17, items_count ), 0xffffffffu, NULL, 1, 0.0, 131071.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w17_info_ = { "W17", (uint32_t) sizeof( W17 ), 1, schema_example_w17_fields_, schema_example_w17_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w26_fields_[] = { + { "items", NULL, "uint32", 0x0000000000000000ull, 8, 1, 1, 0, 6, (uint32_t) offsetof( W26, items ), (uint32_t) sizeof( ( (W26 *) 0 )->items[0] ), (uint32_t) offsetof( W26, items_count ), 0xffffffffu, NULL, 1, 0.0, 6.7108863e+07, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w26_info_ = { "W26", (uint32_t) sizeof( W26 ), 1, schema_example_w26_fields_, schema_example_w26_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w50_fields_[] = { + { "items", NULL, "uint64", 0x0000000000000000ull, 9, 1, 1, 0, 3, (uint32_t) offsetof( W50, items ), (uint32_t) sizeof( ( (W50 *) 0 )->items[0] ), (uint32_t) offsetof( W50, items_count ), 0xffffffffu, NULL, 1, 0.0, 1.125899906842623e+15, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w50_info_ = { "W50", (uint32_t) sizeof( W50 ), 1, schema_example_w50_fields_, schema_example_w50_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_w52_fields_[] = { + { "items", NULL, "uint64", 0x0000000000000000ull, 9, 1, 1, 0, 3, (uint32_t) offsetof( W52, items ), (uint32_t) sizeof( ( (W52 *) 0 )->items[0] ), (uint32_t) offsetof( W52, items_count ), 0xffffffffu, NULL, 1, 0.0, 4.503599627370495e+15, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_w52_info_ = { "W52", (uint32_t) sizeof( W52 ), 1, schema_example_w52_fields_, schema_example_w52_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_example_wide_fields_[] = { + { "w", NULL, "bits(37)", 0x0000000000000000ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( Wide, w ), (uint32_t) sizeof( ( (Wide *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.37438953471e+11, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_example_wide_info_ = { "Wide", (uint32_t) sizeof( Wide ), 1, schema_example_wide_fields_, schema_example_wide_reset_raw_, TableDocNone, 0, NULL }; + +const UnitViewInfo * schema_example_unit_view_(void) +{ + static const ViewVariant MissileType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Heatseeker", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Torpedo", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Nuke", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Pending_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant PropType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Asteroid", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Chunk", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Fragment", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Sphere", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "BlackHole", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 6, "DysonPanel", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ShipType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Fighter", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Corvette", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Bomber", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Destroyer", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 5, "Carrier", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Team_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Red", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Blue", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Weapon_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Laser", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Missile", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Railgun", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ProbeFlags_view_variants[] = { + { 0, "Armed", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Cloaked", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Damaged", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ShipFlags_view_variants[] = { + { 0, "FiringLaser", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Boosting", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Braking", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Aiming", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant DefaultBulkChoice_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "first", 0x0000000000000000ull, "DefaultBulkArm", &schema_example_default_bulk_arm_info_, NULL, TableDocNone, 0, NULL }, + { 2, "second", 0x0000000000000000ull, "DefaultBulkArm", &schema_example_default_bulk_arm_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant DefaultChoice_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "first", 0x0000000000000000ull, "DefaultArm", &schema_example_default_arm_info_, NULL, TableDocNone, 0, NULL }, + { 2, "second", 0x0000000000000000ull, "DefaultArm", &schema_example_default_arm_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant EmptyUnion_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "a", 0x0000000000000000ull, "EmptyA", &schema_example_empty_a_info_, NULL, TableDocNone, 0, NULL }, + { 2, "b", 0x0000000000000000ull, "EmptyB", &schema_example_empty_b_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ProbeShape_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "ring", 0x0000000000000000ull, "ProbeRing", &schema_example_probe_ring_info_, NULL, TableDocNone, 0, NULL }, + { 2, "slab", 0x0000000000000000ull, "ProbeSlab", &schema_example_probe_slab_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Uneven_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "narrow", 0x0000000000000000ull, "Narrow", &schema_example_narrow_info_, NULL, TableDocNone, 0, NULL }, + { 2, "wide", 0x0000000000000000ull, "Wide", &schema_example_wide_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "MissileType", "Enums.schema", 3, 8, 4, MissileType_view_variants, TableDocNone, 0, NULL }, + { "Pending", "Enums.schema", 0, 8, 1, Pending_view_variants, TableDocNone, 0, NULL }, + { "PropType", "Enums.schema", 6, 8, 7, PropType_view_variants, TableDocNone, 0, NULL }, + { "ShipType", "Enums.schema", 5, 8, 6, ShipType_view_variants, TableDocNone, 0, NULL }, + { "Team", "Enums.schema", 2, 8, 3, Team_view_variants, TableDocNone, 0, NULL }, + { "Weapon", "Wire.schema", 15, 8, 4, Weapon_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary flags_decls[] = { + { "ProbeFlags", "Wire.schema", 2, 8, 3, ProbeFlags_view_variants, TableDocNone, 0, NULL }, + { "ShipFlags", "Enums.schema", 3, 8, 4, ShipFlags_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary unions[] = { + { "DefaultBulkChoice", "ArmDefaults.schema", 2, 8, 3, DefaultBulkChoice_view_variants, TableDocNone, 0, NULL }, + { "DefaultChoice", "ArmDefaults.schema", 2, 8, 3, DefaultChoice_view_variants, TableDocNone, 0, NULL }, + { "EmptyUnion", "Clauses.schema", 2, 8, 3, EmptyUnion_view_variants, TableDocNone, 0, NULL }, + { "ProbeShape", "Wire.schema", 2, 8, 3, ProbeShape_view_variants, TableDocNone, 0, NULL }, + { "Uneven", "Joins.schema", 2, 8, 3, Uneven_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "ArmAlign", "Joins.schema", 0, &schema_example_arm_align_info_, TableDocNone, 0, NULL }, + { "ArmArray", "Joins.schema", 0, &schema_example_arm_array_info_, TableDocNone, 0, NULL }, + { "ArmEmpty", "Joins.schema", 0, &schema_example_arm_empty_info_, TableDocNone, 0, NULL }, + { "ArmsAgree", "Joins.schema", 0, &schema_example_arms_agree_info_, TableDocNone, 0, NULL }, + { "ArmsDisagree", "Joins.schema", 0, &schema_example_arms_disagree_info_, TableDocNone, 0, NULL }, + { "ArmsNested", "Joins.schema", 0, &schema_example_arms_nested_info_, TableDocNone, 0, NULL }, + { "ArrEleven", "Clauses.schema", 0, &schema_example_arr_eleven_info_, TableDocNone, 0, NULL }, + { "ArrNested", "Clauses.schema", 0, &schema_example_arr_nested_info_, TableDocNone, 0, NULL }, + { "ArrTri3", "Clauses.schema", 0, &schema_example_arr_tri3_info_, TableDocNone, 0, NULL }, + { "ArrUneven", "Joins.schema", 0, &schema_example_arr_uneven_info_, TableDocNone, 0, NULL }, + { "Block", "Wire.schema", 0, &schema_example_block_info_, TableDocNone, 0, NULL }, + { "Chat", "Wire.schema", 0, &schema_example_chat_info_, TableDocNone, 0, NULL }, + { "CompressedProbe", "Wire.schema", 0, &schema_example_compressed_probe_info_, TableDocNone, 0, NULL }, + { "DefaultArm", "ArmDefaults.schema", 0, &schema_example_default_arm_info_, TableDocNone, 0, NULL }, + { "DefaultBulkArm", "ArmDefaults.schema", 0, &schema_example_default_bulk_arm_info_, TableDocNone, 0, NULL }, + { "DefaultChoicePacket", "ArmDefaults.schema", 0, &schema_example_default_choice_packet_info_, TableDocNone, 0, NULL }, + { "Eleven", "Clauses.schema", 0, &schema_example_eleven_info_, TableDocNone, 0, NULL }, + { "EmptyA", "Clauses.schema", 0, &schema_example_empty_a_info_, TableDocNone, 0, NULL }, + { "EmptyB", "Clauses.schema", 0, &schema_example_empty_b_info_, TableDocNone, 0, NULL }, + { "ExpressionProbe", "Types.schema", 0, &schema_example_expression_probe_info_, TableDocNone, 0, NULL }, + { "ExtremeProbe", "Types.schema", 0, &schema_example_extreme_probe_info_, TableDocNone, 0, NULL }, + { "ExtremeRow", "Types.schema", 0, &schema_example_extreme_row_info_, TableDocNone, 0, NULL }, + { "F13", "Clauses.schema", 0, &schema_example_f13_info_, TableDocNone, 0, NULL }, + { "Handle", "Types.schema", 0, &schema_example_handle_info_, TableDocNone, 0, NULL }, + { "Heartbeat", "Wire.schema", 0, &schema_example_heartbeat_info_, TableDocNone, 0, NULL }, + { "HoldsEmptyUnion", "Clauses.schema", 0, &schema_example_holds_empty_union_info_, TableDocNone, 0, NULL }, + { "HoldsUneven", "Joins.schema", 0, &schema_example_holds_uneven_info_, TableDocNone, 0, NULL }, + { "Input", "Types.schema", 0, &schema_example_input_info_, TableDocNone, 0, NULL }, + { "InputPacket", "Types.schema", 0, &schema_example_input_packet_info_, TableDocNone, 0, NULL }, + { "Narrow", "Joins.schema", 0, &schema_example_narrow_info_, TableDocNone, 0, NULL }, + { "ProbeArray", "Wire.schema", 0, &schema_example_probe_array_info_, TableDocNone, 0, NULL }, + { "ProbeBits", "Wire.schema", 0, &schema_example_probe_bits_info_, TableDocNone, 0, NULL }, + { "ProbeCollider", "Wire.schema", 0, &schema_example_probe_collider_info_, TableDocNone, 0, NULL }, + { "ProbeConfig", "Wire.schema", 0, &schema_example_probe_config_info_, TableDocNone, 0, NULL }, + { "ProbeHeader", "Wire.schema", 0, &schema_example_probe_header_info_, TableDocNone, 0, NULL }, + { "ProbeReport", "Wire.schema", 0, &schema_example_probe_report_info_, TableDocNone, 0, NULL }, + { "ProbeRing", "Wire.schema", 0, &schema_example_probe_ring_info_, TableDocNone, 0, NULL }, + { "ProbeSample", "Wire.schema", 0, &schema_example_probe_sample_info_, TableDocNone, 0, NULL }, + { "ProbeSlab", "Wire.schema", 0, &schema_example_probe_slab_info_, TableDocNone, 0, NULL }, + { "QuantizedPosition", "Types.schema", 0, &schema_example_quantized_position_info_, TableDocNone, 0, NULL }, + { "QuantizedRotation", "Types.schema", 0, &schema_example_quantized_rotation_info_, TableDocNone, 0, NULL }, + { "QuantizedVelocity", "Types.schema", 0, &schema_example_quantized_velocity_info_, TableDocNone, 0, NULL }, + { "Quat", "Types.schema", 0, &schema_example_quat_info_, TableDocNone, 0, NULL }, + { "RegainAfterAlign", "Joins.schema", 0, &schema_example_regain_after_align_info_, TableDocNone, 0, NULL }, + { "RenderBlock", "Render.schema", 0, &schema_example_render_block_info_, TableDocNone, 0, NULL }, + { "RenderSprite", "Render.schema", 0, &schema_example_render_sprite_info_, TableDocNone, 0, NULL }, + { "RigidBody", "Types.schema", 0, &schema_example_rigid_body_info_, TableDocNone, 0, NULL }, + { "ShipCreate", "Types.schema", 0, &schema_example_ship_create_info_, TableDocNone, 0, NULL }, + { "Sole", "Clauses.schema", 0, &schema_example_sole_info_, TableDocNone, 0, NULL }, + { "SpanChunk", "Degenerate.schema", 0, &schema_example_span_chunk_info_, TableDocNone, 0, NULL }, + { "SpanF64", "Degenerate.schema", 0, &schema_example_span_f64_info_, TableDocNone, 0, NULL }, + { "SpanI64", "Degenerate.schema", 0, &schema_example_span_i64_info_, TableDocNone, 0, NULL }, + { "SpanOne", "Degenerate.schema", 0, &schema_example_span_one_info_, TableDocNone, 0, NULL }, + { "SpanTail", "Degenerate.schema", 0, &schema_example_span_tail_info_, TableDocNone, 0, NULL }, + { "SpanTwice", "Degenerate.schema", 0, &schema_example_span_twice_info_, TableDocNone, 0, NULL }, + { "SpanU64", "Degenerate.schema", 0, &schema_example_span_u64_info_, TableDocNone, 0, NULL }, + { "Strs", "Clauses.schema", 0, &schema_example_strs_info_, TableDocNone, 0, NULL }, + { "Test", "Wire.schema", 0, &schema_example_test_info_, TableDocNone, 0, NULL }, + { "TestData", "Wire.schema", 0, &schema_example_test_data_info_, TableDocNone, 0, NULL }, + { "Tri3", "Clauses.schema", 0, &schema_example_tri3_info_, TableDocNone, 0, NULL }, + { "Trio", "Degenerate.schema", 0, &schema_example_trio_info_, TableDocNone, 0, NULL }, + { "TrioFirst", "Degenerate.schema", 0, &schema_example_trio_first_info_, TableDocNone, 0, NULL }, + { "TrioSole", "Degenerate.schema", 0, &schema_example_trio_sole_info_, TableDocNone, 0, NULL }, + { "TrioStraddle", "Degenerate.schema", 0, &schema_example_trio_straddle_info_, TableDocNone, 0, NULL }, + { "Vec2", "Degenerate.schema", 0, &schema_example_vec2_info_, TableDocNone, 0, NULL }, + { "Vec3", "Types.schema", 0, &schema_example_vec3_info_, TableDocNone, 0, NULL }, + { "W1", "Clauses.schema", 0, &schema_example_w1_info_, TableDocNone, 0, NULL }, + { "W13", "Clauses.schema", 0, &schema_example_w13_info_, TableDocNone, 0, NULL }, + { "W17", "Clauses.schema", 0, &schema_example_w17_info_, TableDocNone, 0, NULL }, + { "W26", "Clauses.schema", 0, &schema_example_w26_info_, TableDocNone, 0, NULL }, + { "W50", "Clauses.schema", 0, &schema_example_w50_info_, TableDocNone, 0, NULL }, + { "W52", "Clauses.schema", 0, &schema_example_w52_info_, TableDocNone, 0, NULL }, + { "Wide", "Joins.schema", 0, &schema_example_wide_info_, TableDocNone, 0, NULL }, + }; + static const ViewConstant constants[] = { + { "CeilingCount", "Types.schema", "uint64", 0, (int64_t) 0xffffffffffffffffull, 0.0, TableDocNone, 0, NULL }, + { "FloorLimit", "Types.schema", "int64", 0, (-INT64_C(9223372036854775807)-1), 0.0, TableDocNone, 0, NULL }, + { "HalfTurn", "Wire.schema", "float64", 1, 0, 180.0, TableDocNone, 0, NULL }, + { "MaxBlockSize", "Constants.schema", "int64", 0, INT64_C(2000), 0.0, TableDocNone, 0, NULL }, + { "MaxChatLength", "Constants.schema", "int64", 0, INT64_C(256), 0.0, TableDocNone, 0, NULL }, + { "MaxHealth", "Constants.schema", "int64", 0, INT64_C(1000), 0.0, TableDocNone, 0, NULL }, + { "MaxInputsPerPacket", "Constants.schema", "int64", 0, INT64_C(16), 0.0, TableDocNone, 0, NULL }, + { "MaxObjects", "Constants.schema", "int64", 0, INT64_C(10000), 0.0, TableDocNone, 0, NULL }, + { "MaxPositionUnits", "Constants.schema", "int64", 0, INT64_C(8388608), 0.0, TableDocNone, 0, NULL }, + { "MaxSpeedMeters", "Constants.schema", "int64", 0, INT64_C(2048), 0.0, TableDocNone, 0, NULL }, + { "MaxVelocityUnits", "Constants.schema", "int64", 0, INT64_C(2097152), 0.0, TableDocNone, 0, NULL }, + { "MaxWorldMeters", "Constants.schema", "int64", 0, INT64_C(8192), 0.0, TableDocNone, 0, NULL }, + { "NumTeams", "Constants.schema", "int64", 0, INT64_C(2), 0.0, TableDocNone, 0, NULL }, + { "PositionUnits", "Constants.schema", "int64", 0, INT64_C(1024), 0.0, TableDocNone, 0, NULL }, + { "ProbeId", "Wire.schema", "uint64", 0, INT64_C(244837814094590), 0.0, TableDocNone, 0, NULL }, + { "RenderBlockMaxSprites", "Render.schema", "int64", 0, INT64_C(64), 0.0, TableDocNone, 0, NULL }, + { "RotationUnits", "Constants.schema", "int64", 0, INT64_C(1024), 0.0, TableDocNone, 0, NULL }, + { "ShipMaxLasers", "Constants.schema", "int64", 0, INT64_C(16), 0.0, TableDocNone, 0, NULL }, + { "ShipMaxMissiles", "Constants.schema", "int64", 0, INT64_C(16), 0.0, TableDocNone, 0, NULL }, + { "SpinRate", "Wire.schema", "float64", 1, 0, 1.5, TableDocNone, 0, NULL }, + { "TickRate", "Wire.schema", "float32", 1, 0, 60.0, TableDocNone, 0, NULL }, + { "VelocityUnits", "Constants.schema", "int64", 0, INT64_C(1024), 0.0, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "example", 0x8656ae68c06b97a7ull, + 73, types, + 0, NULL, + 6, enums, + 2, flags_decls, + 5, unions, + 22, constants }; + return &info; +} diff --git a/testdata/golden/c/ExampleView.h b/testdata/golden/c/ExampleView.h new file mode 100644 index 000000000..634d2bc36 --- /dev/null +++ b/testdata/golden/c/ExampleView.h @@ -0,0 +1,1329 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_EXAMPLE_VIEW_H +#define SCHEMA_EXAMPLE_VIEW_H +#include +#include +#include +#include "ArmDefaults.h" +#include "Clauses.h" +#include "Constants.h" +#include "Degenerate.h" +#include "Enums.h" +#include "Joins.h" +#include "Render.h" +#include "Types.h" +#include "Wire.h" +#ifndef SCHEMA_EXAMPLE_TABLE_PRIMITIVES +#define SCHEMA_EXAMPLE_TABLE_PRIMITIVES + +/* THE CODEC DOES NOT DEPEND ON THE COMPILER'S INLINING BUDGET. A table of a + realistic field count emits one large body per type, and the cursor a body + writes through lives in the caller's TableWriter: across a call boundary that + cursor round-trips through memory, and a uint8_t * store may alias the writer + itself, so every put reloads it. When a budget runs out mid-body the codec + silently degrades to that shape. Forcing the primitives and the fixed-class + bodies inline is what keeps the cursor in registers and lets adjacent + constant framing bytes merge into one store. + + The spelling is FEATURE TESTED rather than assumed, exactly as the packet + emitter's own inlining demand is (SPEC §6.1's C column). + + IT CARRIES THE PACKAGE for the reason every other name here does, and that + reason is the LINKER rather than the preprocessor: two units of the same + schema family link into one program — two generations of one schema is the + case the conformance driver itself is — so nothing this backend emits may + be spelled the same way by two packages. They do NOT share a translation + unit and cannot: C has no namespace, so two units' headers in one TU + redefine TableReport, TableWriter and every other runtime name, which is the + packet emitter's standing limit (SPEC §6.1) and not something tables + changed. A per-package macro is what keeps the LINK legal. */ +#ifndef SCHEMA_EXAMPLE_TABLE_INLINE +#if defined( _MSC_VER ) +#define SCHEMA_EXAMPLE_TABLE_INLINE __forceinline +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_EXAMPLE_TABLE_INLINE inline __attribute__(( always_inline )) +#else +#define SCHEMA_EXAMPLE_TABLE_INLINE inline +#endif +#endif + +/* A COMPILE-TIME assertion under -std=c99, which has none of its own. C11's + _Static_assert carries the message into the build log verbatim and is used + wherever the compiler has it; everywhere else the negative array bound is + the same refusal with a tag a reader can find. The tag is unique per record + and field, and each record is asserted by the ONE header that declares it, + so no two of these ever meet. */ +#ifndef SCHEMA_TABLE_STATIC_ASSERT +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) _Static_assert( cond, message ) +#elif defined( __cplusplus ) && __cplusplus >= 201103L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) static_assert( cond, message ) +#else +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) typedef char schema_table_assert_##tag[ ( cond ) ? 1 : -1 ] +#endif +#endif + +/* A record's ALIGNMENT, which the cook's layout contract asserts and which the + cook's Open compares the header's alignment word against (§7.1, §20.3). + C99 has no alignof either; C11's _Alignof, the two GNU spellings and MSVC's + cover every compiler this repo builds under, and the last form is the + portable definition of the same number. */ +#ifndef SCHEMA_TABLE_ALIGNOF +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_ALIGNOF( T ) _Alignof( T ) +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof__( T ) +#elif defined( _MSC_VER ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof( T ) +#else +#define SCHEMA_TABLE_ALIGNOF( T ) ( (size_t) offsetof( struct { char schema_pad_; T schema_member_; }, schema_member_ ) ) +#endif +#endif + +/* The table-wire read report — the permissive contract's ledger. Silence + (all zero) means the data matched this reader's schema exactly. */ +typedef struct TableReport +{ + int32_t unknown; /* unknown field ids skipped (newer data) */ + int32_t kind_mismatch; /* known id, changed type — skipped, never misdecoded */ + int32_t clamped; /* out-of-range values clamped to declared bounds */ + /* a key the TEXT form saw twice: last wins, and the repeat is counted + (docs/SPEC-TABLES.md §16.2). The wire never raises it — a body carrying an + id twice is legal input whose last occurrence wins, silently (§3). */ + int32_t duplicate; + int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ +} TableReport; + +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + +/* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- + + Static field descriptors for every type in the table closure: name, wire + id/kind, storage offset, bounds, ranges, enum names and branch guards — + enough to walk, print, diff, edit or bind any table value at runtime with + no schema files. _table_type() returns 's descriptor. */ + +struct TableTypeInfo; +struct TableFieldInfo; + +/* One arm of a union field: where its payload sits inside the union's storage + and what its payload looks like. The arm's NAME and its table-wire id ride + in the field's own vocabulary table at the same tag, so nothing is spelled + twice (docs/SPEC-TABLES.md §8). */ +typedef struct TableUnionArmInfo +{ + uint32_t offset; /* offsetof the arm's payload within the union storage */ + const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ +} TableUnionArmInfo; + +/* A union field's shape: the tag, and the arms indexed by it. Arms run + [0, enum_max]; index 0 is the EMPTY arm and carries no payload. */ +typedef struct TableUnionInfo +{ + uint32_t tag_offset; /* offsetof the tag within the union storage */ + uint32_t tag_size; /* sizeof the tag */ + const TableUnionArmInfo * arms; +} TableUnionInfo; + +/* ONE ENTRY OF A VOCABULARY, and the whole of what C++ spells as a pair of + captureless lambdas. An enum's values, a union's arms and a flags field's + BITS are each a named set indexed by [0, enum_max], so the set rides as an + ARRAY indexed the same way: no function per enum, and therefore no name per + enum for a schema to collide with (docs/SPEC-TABLES.md §11). + + name is the variant's spelling, or NULL for a value the declared set does + not name. id is the TABLE-WIRE id it rides under (§5) — the hash of the + variant's name, 0 for the reserved None and for the empty union arm. A + FLAGS field's variants are BIT POSITIONS and have no per-variant wire id + (§4), so has_ids is 0 there and 1 for an enum and a union: a vocabulary + with names and no ids is what says "flags". */ +typedef struct TableVariantInfo +{ + const char * name; + uint64_t id; +} TableVariantInfo; + +/* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// + block carries a doc column pointing at this one object, so absence costs a + unit no string data and a printer concatenates doc columns with no null + test. One definition per translation unit, and the descriptors of a unit + are one, so every absent doc compares equal by address. */ +static SCHEMA_UNUSED const char TableDocNone[1] = ""; + +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + +typedef struct TableFieldInfo +{ + const char * name; /* schema field name, e.g. "health" */ + const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ + const char * type_name; /* schema type name, e.g. "float32", "Grade" */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ + int is_array; /* fixed or counted array (bytes included) */ + int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ + int optional; /* a ?T field: a _present companion decides whether it rides */ + int32_t array_bound; /* array capacity / string max length; 0 for plain scalars */ + uint32_t offset; /* offsetof the storage member */ + uint32_t elem_size; /* sizeof the member (element size for arrays) */ + uint32_t count_offset; /* offsetof the _count/_length companion, or 0xffffffff */ + uint32_t present_offset; /* offsetof the _present companion, or 0xffffffff */ + const struct TableTypeInfo * table; /* nested table's descriptor, or NULL */ + int has_range; /* a declared [min, max] (int or float) */ + double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ + double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; + int64_t enum_max; /* enums: highest valid value (None = 0 always valid); + unions: the arm count (tag range [0, enum_max]); + flags: the highest declared BIT INDEX; else -1 */ + /* the vocabulary, indexed the same way enum_max bounds: an enum's value -> + (name, id), a union's tag -> (arm name, arm id), a FLAGS field's bit + index -> (variant name, 0). NULL for every other kind. */ + const TableVariantInfo * variants; + int has_variant_ids; /* 1 for an enum or a union, 0 for flags (§4) */ + /* an ENUM-KEYED array (docs/SPEC-TABLES.md §2.4): the array has one slot per + variant of key_type_name, the key k at index k-1, and its slots ride + under variant ids rather than positions. keys is the key's vocabulary, + indexed by the KEY — walk [0, array_bound) and ask about index + 1 to + print slots by name. NULL on every other field. */ + const char * key_type_name; + const TableVariantInfo * keys; + int64_t key_max; /* the key enum's highest value, bounding keys */ + /* union fields: the tag and its arms. NULL for every other kind. */ + const TableUnionInfo * arms; + const char * guard; /* branch guard, e.g. "at_rest" or "!at_rest"; "" if unguarded */ + /* what a PERSON wrote about the field (docs/SPEC-TABLES.md §8.1): the /// + block above it, verbatim (SPEC §4.1). It is TableDocNone when there is + none, never NULL. Its tags (SPEC §4.2) follow in declared order, and an + untagged field is 0 beside NULL. Static, constant-initialized, + allocating nothing. */ + const char * doc; + int32_t num_tags; + const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); +} TableFieldInfo; + +typedef struct TableTypeInfo +{ + const char * name; /* schema type name */ + uint32_t size; /* sizeof the storage struct */ + int32_t num_fields; + const TableFieldInfo * fields; + /* put one instance back at its declared defaults, in place. A generic + walker that fills a value has to be able to establish the defaults an + absent field takes, and it holds no type to spell — this is the one + thing the descriptors could not express without it. It is the void * + form of _reset and the same code. */ + void (*reset)( void * storage ); + /* the declaration's own doc and tags, on the same terms as a field's + (docs/SPEC-TABLES.md §8.1) */ + const char * doc; + int32_t num_tags; + const char * const * tags; +} TableTypeInfo; + + + +static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } +static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } +static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } +static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } + +#endif /* SCHEMA_EXAMPLE_TABLE_PRIMITIVES */ + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void arm_align_reset( ArmAlign * value ); +static SCHEMA_UNUSED void schema_example_arm_align_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arm_array_reset( ArmArray * value ); +static SCHEMA_UNUSED void schema_example_arm_array_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arm_empty_reset( ArmEmpty * value ); +static SCHEMA_UNUSED void schema_example_arm_empty_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arms_agree_reset( ArmsAgree * value ); +static SCHEMA_UNUSED void schema_example_arms_agree_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arms_disagree_reset( ArmsDisagree * value ); +static SCHEMA_UNUSED void schema_example_arms_disagree_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arms_nested_reset( ArmsNested * value ); +static SCHEMA_UNUSED void schema_example_arms_nested_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arr_eleven_reset( ArrEleven * value ); +static SCHEMA_UNUSED void schema_example_arr_eleven_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arr_nested_reset( ArrNested * value ); +static SCHEMA_UNUSED void schema_example_arr_nested_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arr_tri3_reset( ArrTri3 * value ); +static SCHEMA_UNUSED void schema_example_arr_tri3_reset_raw_( void * storage ); +static SCHEMA_UNUSED void arr_uneven_reset( ArrUneven * value ); +static SCHEMA_UNUSED void schema_example_arr_uneven_reset_raw_( void * storage ); +static SCHEMA_UNUSED void block_reset( Block * value ); +static SCHEMA_UNUSED void schema_example_block_reset_raw_( void * storage ); +static SCHEMA_UNUSED void chat_reset( Chat * value ); +static SCHEMA_UNUSED void schema_example_chat_reset_raw_( void * storage ); +static SCHEMA_UNUSED void compressed_probe_reset( CompressedProbe * value ); +static SCHEMA_UNUSED void schema_example_compressed_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void default_arm_reset( DefaultArm * value ); +static SCHEMA_UNUSED void schema_example_default_arm_reset_raw_( void * storage ); +static SCHEMA_UNUSED void default_bulk_arm_reset( DefaultBulkArm * value ); +static SCHEMA_UNUSED void schema_example_default_bulk_arm_reset_raw_( void * storage ); +static SCHEMA_UNUSED void default_choice_packet_reset( DefaultChoicePacket * value ); +static SCHEMA_UNUSED void schema_example_default_choice_packet_reset_raw_( void * storage ); +static SCHEMA_UNUSED void eleven_reset( Eleven * value ); +static SCHEMA_UNUSED void schema_example_eleven_reset_raw_( void * storage ); +static SCHEMA_UNUSED void empty_a_reset( EmptyA * value ); +static SCHEMA_UNUSED void schema_example_empty_a_reset_raw_( void * storage ); +static SCHEMA_UNUSED void empty_b_reset( EmptyB * value ); +static SCHEMA_UNUSED void schema_example_empty_b_reset_raw_( void * storage ); +static SCHEMA_UNUSED void expression_probe_reset( ExpressionProbe * value ); +static SCHEMA_UNUSED void schema_example_expression_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void extreme_probe_reset( ExtremeProbe * value ); +static SCHEMA_UNUSED void schema_example_extreme_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void extreme_row_reset( ExtremeRow * value ); +static SCHEMA_UNUSED void schema_example_extreme_row_reset_raw_( void * storage ); +static SCHEMA_UNUSED void f13_reset( F13 * value ); +static SCHEMA_UNUSED void schema_example_f13_reset_raw_( void * storage ); +static SCHEMA_UNUSED void handle_reset( Handle * value ); +static SCHEMA_UNUSED void schema_example_handle_reset_raw_( void * storage ); +static SCHEMA_UNUSED void heartbeat_reset( Heartbeat * value ); +static SCHEMA_UNUSED void schema_example_heartbeat_reset_raw_( void * storage ); +static SCHEMA_UNUSED void holds_empty_union_reset( HoldsEmptyUnion * value ); +static SCHEMA_UNUSED void schema_example_holds_empty_union_reset_raw_( void * storage ); +static SCHEMA_UNUSED void holds_uneven_reset( HoldsUneven * value ); +static SCHEMA_UNUSED void schema_example_holds_uneven_reset_raw_( void * storage ); +static SCHEMA_UNUSED void input_reset( Input * value ); +static SCHEMA_UNUSED void schema_example_input_reset_raw_( void * storage ); +static SCHEMA_UNUSED void input_packet_reset( InputPacket * value ); +static SCHEMA_UNUSED void schema_example_input_packet_reset_raw_( void * storage ); +static SCHEMA_UNUSED void narrow_reset( Narrow * value ); +static SCHEMA_UNUSED void schema_example_narrow_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_array_reset( ProbeArray * value ); +static SCHEMA_UNUSED void schema_example_probe_array_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_bits_reset( ProbeBits * value ); +static SCHEMA_UNUSED void schema_example_probe_bits_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_collider_reset( ProbeCollider * value ); +static SCHEMA_UNUSED void schema_example_probe_collider_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_config_reset( ProbeConfig * value ); +static SCHEMA_UNUSED void schema_example_probe_config_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_header_reset( ProbeHeader * value ); +static SCHEMA_UNUSED void schema_example_probe_header_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_report_reset( ProbeReport * value ); +static SCHEMA_UNUSED void schema_example_probe_report_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_ring_reset( ProbeRing * value ); +static SCHEMA_UNUSED void schema_example_probe_ring_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_sample_reset( ProbeSample * value ); +static SCHEMA_UNUSED void schema_example_probe_sample_reset_raw_( void * storage ); +static SCHEMA_UNUSED void probe_slab_reset( ProbeSlab * value ); +static SCHEMA_UNUSED void schema_example_probe_slab_reset_raw_( void * storage ); +static SCHEMA_UNUSED void quantized_position_reset( QuantizedPosition * value ); +static SCHEMA_UNUSED void schema_example_quantized_position_reset_raw_( void * storage ); +static SCHEMA_UNUSED void quantized_rotation_reset( QuantizedRotation * value ); +static SCHEMA_UNUSED void schema_example_quantized_rotation_reset_raw_( void * storage ); +static SCHEMA_UNUSED void quantized_velocity_reset( QuantizedVelocity * value ); +static SCHEMA_UNUSED void schema_example_quantized_velocity_reset_raw_( void * storage ); +static SCHEMA_UNUSED void quat_reset( Quat * value ); +static SCHEMA_UNUSED void schema_example_quat_reset_raw_( void * storage ); +static SCHEMA_UNUSED void regain_after_align_reset( RegainAfterAlign * value ); +static SCHEMA_UNUSED void schema_example_regain_after_align_reset_raw_( void * storage ); +static SCHEMA_UNUSED void render_block_reset( RenderBlock * value ); +static SCHEMA_UNUSED void schema_example_render_block_reset_raw_( void * storage ); +static SCHEMA_UNUSED void render_sprite_reset( RenderSprite * value ); +static SCHEMA_UNUSED void schema_example_render_sprite_reset_raw_( void * storage ); +static SCHEMA_UNUSED void rigid_body_reset( RigidBody * value ); +static SCHEMA_UNUSED void schema_example_rigid_body_reset_raw_( void * storage ); +static SCHEMA_UNUSED void ship_create_reset( ShipCreate * value ); +static SCHEMA_UNUSED void schema_example_ship_create_reset_raw_( void * storage ); +static SCHEMA_UNUSED void sole_reset( Sole * value ); +static SCHEMA_UNUSED void schema_example_sole_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_chunk_reset( SpanChunk * value ); +static SCHEMA_UNUSED void schema_example_span_chunk_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_f64_reset( SpanF64 * value ); +static SCHEMA_UNUSED void schema_example_span_f64_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_i64_reset( SpanI64 * value ); +static SCHEMA_UNUSED void schema_example_span_i64_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_one_reset( SpanOne * value ); +static SCHEMA_UNUSED void schema_example_span_one_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_tail_reset( SpanTail * value ); +static SCHEMA_UNUSED void schema_example_span_tail_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_twice_reset( SpanTwice * value ); +static SCHEMA_UNUSED void schema_example_span_twice_reset_raw_( void * storage ); +static SCHEMA_UNUSED void span_u64_reset( SpanU64 * value ); +static SCHEMA_UNUSED void schema_example_span_u64_reset_raw_( void * storage ); +static SCHEMA_UNUSED void strs_reset( Strs * value ); +static SCHEMA_UNUSED void schema_example_strs_reset_raw_( void * storage ); +static SCHEMA_UNUSED void test_reset( Test * value ); +static SCHEMA_UNUSED void schema_example_test_reset_raw_( void * storage ); +static SCHEMA_UNUSED void test_data_reset( TestData * value ); +static SCHEMA_UNUSED void schema_example_test_data_reset_raw_( void * storage ); +static SCHEMA_UNUSED void tri3_reset( Tri3 * value ); +static SCHEMA_UNUSED void schema_example_tri3_reset_raw_( void * storage ); +static SCHEMA_UNUSED void trio_reset( Trio * value ); +static SCHEMA_UNUSED void schema_example_trio_reset_raw_( void * storage ); +static SCHEMA_UNUSED void trio_first_reset( TrioFirst * value ); +static SCHEMA_UNUSED void schema_example_trio_first_reset_raw_( void * storage ); +static SCHEMA_UNUSED void trio_sole_reset( TrioSole * value ); +static SCHEMA_UNUSED void schema_example_trio_sole_reset_raw_( void * storage ); +static SCHEMA_UNUSED void trio_straddle_reset( TrioStraddle * value ); +static SCHEMA_UNUSED void schema_example_trio_straddle_reset_raw_( void * storage ); +static SCHEMA_UNUSED void vec2_reset( Vec2 * value ); +static SCHEMA_UNUSED void schema_example_vec2_reset_raw_( void * storage ); +static SCHEMA_UNUSED void vec3_reset( Vec3 * value ); +static SCHEMA_UNUSED void schema_example_vec3_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w1_reset( W1 * value ); +static SCHEMA_UNUSED void schema_example_w1_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w13_reset( W13 * value ); +static SCHEMA_UNUSED void schema_example_w13_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w17_reset( W17 * value ); +static SCHEMA_UNUSED void schema_example_w17_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w26_reset( W26 * value ); +static SCHEMA_UNUSED void schema_example_w26_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w50_reset( W50 * value ); +static SCHEMA_UNUSED void schema_example_w50_reset_raw_( void * storage ); +static SCHEMA_UNUSED void w52_reset( W52 * value ); +static SCHEMA_UNUSED void schema_example_w52_reset_raw_( void * storage ); +static SCHEMA_UNUSED void wide_reset( Wide * value ); +static SCHEMA_UNUSED void schema_example_wide_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void arm_align_reset( ArmAlign * value ) +{ + value->lead = 0; + value->flag = 0; + memset( value->s, 0, sizeof( value->s ) ); + value->s_length = 0; + value->b = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arm_align_reset_raw_( void * storage ) { arm_align_reset( (ArmAlign *) storage ); } + +extern const TableTypeInfo schema_example_arm_align_info_; +static SCHEMA_UNUSED const TableTypeInfo * arm_align_table_type(void) { return &schema_example_arm_align_info_; } +static SCHEMA_UNUSED void arm_array_reset( ArmArray * value ) +{ + value->lead = 0; + value->flag = 0; + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; + value->b = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arm_array_reset_raw_( void * storage ) { arm_array_reset( (ArmArray *) storage ); } + +extern const TableTypeInfo schema_example_arm_array_info_; +static SCHEMA_UNUSED const TableTypeInfo * arm_array_table_type(void) { return &schema_example_arm_array_info_; } +static SCHEMA_UNUSED void arm_empty_reset( ArmEmpty * value ) +{ + value->lead = 0; + value->flag = 0; + value->a = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arm_empty_reset_raw_( void * storage ) { arm_empty_reset( (ArmEmpty *) storage ); } + +extern const TableTypeInfo schema_example_arm_empty_info_; +static SCHEMA_UNUSED const TableTypeInfo * arm_empty_table_type(void) { return &schema_example_arm_empty_info_; } +static SCHEMA_UNUSED void arms_agree_reset( ArmsAgree * value ) +{ + value->lead = 0; + value->flag = 0; + value->a = 0; + value->b = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arms_agree_reset_raw_( void * storage ) { arms_agree_reset( (ArmsAgree *) storage ); } + +extern const TableTypeInfo schema_example_arms_agree_info_; +static SCHEMA_UNUSED const TableTypeInfo * arms_agree_table_type(void) { return &schema_example_arms_agree_info_; } +static SCHEMA_UNUSED void arms_disagree_reset( ArmsDisagree * value ) +{ + value->lead = 0; + value->flag = 0; + value->a = 0; + value->b = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arms_disagree_reset_raw_( void * storage ) { arms_disagree_reset( (ArmsDisagree *) storage ); } + +extern const TableTypeInfo schema_example_arms_disagree_info_; +static SCHEMA_UNUSED const TableTypeInfo * arms_disagree_table_type(void) { return &schema_example_arms_disagree_info_; } +static SCHEMA_UNUSED void arms_nested_reset( ArmsNested * value ) +{ + value->lead = 0; + value->outer = 0; + value->inner = 0; + value->x = 0; + value->y = 0; + value->z = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arms_nested_reset_raw_( void * storage ) { arms_nested_reset( (ArmsNested *) storage ); } + +extern const TableTypeInfo schema_example_arms_nested_info_; +static SCHEMA_UNUSED const TableTypeInfo * arms_nested_table_type(void) { return &schema_example_arms_nested_info_; } +static SCHEMA_UNUSED void arr_eleven_reset( ArrEleven * value ) +{ + eleven_reset( &value->items[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 9 ); i++ ) { value->items[i] = value->items[0]; } } +} + +static SCHEMA_UNUSED void schema_example_arr_eleven_reset_raw_( void * storage ) { arr_eleven_reset( (ArrEleven *) storage ); } + +extern const TableTypeInfo schema_example_arr_eleven_info_; +static SCHEMA_UNUSED const TableTypeInfo * arr_eleven_table_type(void) { return &schema_example_arr_eleven_info_; } +static SCHEMA_UNUSED void arr_nested_reset( ArrNested * value ) +{ + value->lead = 0; + eleven_reset( &value->items[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 4 ); i++ ) { value->items[i] = value->items[0]; } } + value->items_count = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arr_nested_reset_raw_( void * storage ) { arr_nested_reset( (ArrNested *) storage ); } + +extern const TableTypeInfo schema_example_arr_nested_info_; +static SCHEMA_UNUSED const TableTypeInfo * arr_nested_table_type(void) { return &schema_example_arr_nested_info_; } +static SCHEMA_UNUSED void arr_tri3_reset( ArrTri3 * value ) +{ + tri3_reset( &value->items[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 10 ); i++ ) { value->items[i] = value->items[0]; } } + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_arr_tri3_reset_raw_( void * storage ) { arr_tri3_reset( (ArrTri3 *) storage ); } + +extern const TableTypeInfo schema_example_arr_tri3_info_; +static SCHEMA_UNUSED const TableTypeInfo * arr_tri3_table_type(void) { return &schema_example_arr_tri3_info_; } +static SCHEMA_UNUSED void arr_uneven_reset( ArrUneven * value ) +{ + value->lead = 0; + memset( &value->items[0], 0, sizeof( value->items[0] ) ); + { int32_t i; for ( i = 1; i < (int32_t) ( 3 ); i++ ) { value->items[i] = value->items[0]; } } + value->items_count = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_arr_uneven_reset_raw_( void * storage ) { arr_uneven_reset( (ArrUneven *) storage ); } + +extern const TableTypeInfo schema_example_arr_uneven_info_; +static SCHEMA_UNUSED const TableTypeInfo * arr_uneven_table_type(void) { return &schema_example_arr_uneven_info_; } +static SCHEMA_UNUSED void block_reset( Block * value ) +{ + memset( value->data, 0, sizeof( value->data ) ); + value->data_length = 0; +} + +static SCHEMA_UNUSED void schema_example_block_reset_raw_( void * storage ) { block_reset( (Block *) storage ); } + +extern const TableTypeInfo schema_example_block_info_; +static SCHEMA_UNUSED const TableTypeInfo * block_table_type(void) { return &schema_example_block_info_; } +static SCHEMA_UNUSED void chat_reset( Chat * value ) +{ + memset( value->text, 0, sizeof( value->text ) ); + value->text_length = 0; +} + +static SCHEMA_UNUSED void schema_example_chat_reset_raw_( void * storage ) { chat_reset( (Chat *) storage ); } + +extern const TableTypeInfo schema_example_chat_info_; +static SCHEMA_UNUSED const TableTypeInfo * chat_table_type(void) { return &schema_example_chat_info_; } +static SCHEMA_UNUSED void compressed_probe_reset( CompressedProbe * value ) +{ + value->boundary = 0.0f; + value->offset = 0.0f; +} + +static SCHEMA_UNUSED void schema_example_compressed_probe_reset_raw_( void * storage ) { compressed_probe_reset( (CompressedProbe *) storage ); } + +extern const TableTypeInfo schema_example_compressed_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * compressed_probe_table_type(void) { return &schema_example_compressed_probe_info_; } +static SCHEMA_UNUSED void default_arm_reset( DefaultArm * value ) +{ + probe_config_reset( &value->entries[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 2 ); i++ ) { value->entries[i] = value->entries[0]; } } + value->entries_count = 0; + value->marker = 0; +} + +static SCHEMA_UNUSED void schema_example_default_arm_reset_raw_( void * storage ) { default_arm_reset( (DefaultArm *) storage ); } + +extern const TableTypeInfo schema_example_default_arm_info_; +static SCHEMA_UNUSED const TableTypeInfo * default_arm_table_type(void) { return &schema_example_default_arm_info_; } +static SCHEMA_UNUSED void default_bulk_arm_reset( DefaultBulkArm * value ) +{ + default_arm_reset( &value->payload ); + memset( value->data, 0, sizeof( value->data ) ); + value->data_length = 0; +} + +static SCHEMA_UNUSED void schema_example_default_bulk_arm_reset_raw_( void * storage ) { default_bulk_arm_reset( (DefaultBulkArm *) storage ); } + +extern const TableTypeInfo schema_example_default_bulk_arm_info_; +static SCHEMA_UNUSED const TableTypeInfo * default_bulk_arm_table_type(void) { return &schema_example_default_bulk_arm_info_; } +static SCHEMA_UNUSED void default_choice_packet_reset( DefaultChoicePacket * value ) +{ + memset( &value->choice, 0, sizeof( value->choice ) ); +} + +static SCHEMA_UNUSED void schema_example_default_choice_packet_reset_raw_( void * storage ) { default_choice_packet_reset( (DefaultChoicePacket *) storage ); } + +extern const TableTypeInfo schema_example_default_choice_packet_info_; +static SCHEMA_UNUSED const TableTypeInfo * default_choice_packet_table_type(void) { return &schema_example_default_choice_packet_info_; } +static SCHEMA_UNUSED void eleven_reset( Eleven * value ) +{ + value->a = 0; + value->b = 0; +} + +static SCHEMA_UNUSED void schema_example_eleven_reset_raw_( void * storage ) { eleven_reset( (Eleven *) storage ); } + +extern const TableTypeInfo schema_example_eleven_info_; +static SCHEMA_UNUSED const TableTypeInfo * eleven_table_type(void) { return &schema_example_eleven_info_; } +static SCHEMA_UNUSED void empty_a_reset( EmptyA * value ) +{ + memset( value, 0, sizeof( *value ) ); +} + +static SCHEMA_UNUSED void schema_example_empty_a_reset_raw_( void * storage ) { empty_a_reset( (EmptyA *) storage ); } + +extern const TableTypeInfo schema_example_empty_a_info_; +static SCHEMA_UNUSED const TableTypeInfo * empty_a_table_type(void) { return &schema_example_empty_a_info_; } +static SCHEMA_UNUSED void empty_b_reset( EmptyB * value ) +{ + memset( value, 0, sizeof( *value ) ); +} + +static SCHEMA_UNUSED void schema_example_empty_b_reset_raw_( void * storage ) { empty_b_reset( (EmptyB *) storage ); } + +extern const TableTypeInfo schema_example_empty_b_info_; +static SCHEMA_UNUSED const TableTypeInfo * empty_b_table_type(void) { return &schema_example_empty_b_info_; } +static SCHEMA_UNUSED void expression_probe_reset( ExpressionProbe * value ) +{ + value->hardpoint_index = 0; + value->spin_rate = 1024; +} + +static SCHEMA_UNUSED void schema_example_expression_probe_reset_raw_( void * storage ) { expression_probe_reset( (ExpressionProbe *) storage ); } + +extern const TableTypeInfo schema_example_expression_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * expression_probe_table_type(void) { return &schema_example_expression_probe_info_; } +static SCHEMA_UNUSED void extreme_probe_reset( ExtremeProbe * value ) +{ + value->floor_bound = 0; + value->doubled_floor = 0; + value->ceiling_range = 1ull; + value->floor_default = ( -9223372036854775807ll - 1 ); + value->ceiling_default = 18446744073709551615ull; +} + +static SCHEMA_UNUSED void schema_example_extreme_probe_reset_raw_( void * storage ) { extreme_probe_reset( (ExtremeProbe *) storage ); } + +extern const TableTypeInfo schema_example_extreme_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * extreme_probe_table_type(void) { return &schema_example_extreme_probe_info_; } +static SCHEMA_UNUSED void extreme_row_reset( ExtremeRow * value ) +{ + value->clamped_floor = 0; + value->clamped_ceiling = 1ull; + value->floor_def = ( -9223372036854775807ll - 1 ); + value->ceiling_def = 18446744073709551615ull; +} + +static SCHEMA_UNUSED void schema_example_extreme_row_reset_raw_( void * storage ) { extreme_row_reset( (ExtremeRow *) storage ); } + +extern const TableTypeInfo schema_example_extreme_row_info_; +static SCHEMA_UNUSED const TableTypeInfo * extreme_row_table_type(void) { return &schema_example_extreme_row_info_; } +static SCHEMA_UNUSED void f13_reset( F13 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); +} + +static SCHEMA_UNUSED void schema_example_f13_reset_raw_( void * storage ) { f13_reset( (F13 *) storage ); } + +extern const TableTypeInfo schema_example_f13_info_; +static SCHEMA_UNUSED const TableTypeInfo * f13_table_type(void) { return &schema_example_f13_info_; } +static SCHEMA_UNUSED void handle_reset( Handle * value ) +{ + value->object_id = 0; + value->object_sequence = 0; +} + +static SCHEMA_UNUSED void schema_example_handle_reset_raw_( void * storage ) { handle_reset( (Handle *) storage ); } + +extern const TableTypeInfo schema_example_handle_info_; +static SCHEMA_UNUSED const TableTypeInfo * handle_table_type(void) { return &schema_example_handle_info_; } +static SCHEMA_UNUSED void heartbeat_reset( Heartbeat * value ) +{ + memset( value, 0, sizeof( *value ) ); +} + +static SCHEMA_UNUSED void schema_example_heartbeat_reset_raw_( void * storage ) { heartbeat_reset( (Heartbeat *) storage ); } + +extern const TableTypeInfo schema_example_heartbeat_info_; +static SCHEMA_UNUSED const TableTypeInfo * heartbeat_table_type(void) { return &schema_example_heartbeat_info_; } +static SCHEMA_UNUSED void holds_empty_union_reset( HoldsEmptyUnion * value ) +{ + value->lead = 0; + memset( &value->u, 0, sizeof( value->u ) ); + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_holds_empty_union_reset_raw_( void * storage ) { holds_empty_union_reset( (HoldsEmptyUnion *) storage ); } + +extern const TableTypeInfo schema_example_holds_empty_union_info_; +static SCHEMA_UNUSED const TableTypeInfo * holds_empty_union_table_type(void) { return &schema_example_holds_empty_union_info_; } +static SCHEMA_UNUSED void holds_uneven_reset( HoldsUneven * value ) +{ + value->lead = 0; + memset( &value->u, 0, sizeof( value->u ) ); + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_holds_uneven_reset_raw_( void * storage ) { holds_uneven_reset( (HoldsUneven *) storage ); } + +extern const TableTypeInfo schema_example_holds_uneven_info_; +static SCHEMA_UNUSED const TableTypeInfo * holds_uneven_table_type(void) { return &schema_example_holds_uneven_info_; } +static SCHEMA_UNUSED void input_reset( Input * value ) +{ + value->stick_x = 0.0f; + value->stick_y = 0.0f; + value->throttle = 0.0f; + value->yaw = 0.0f; + value->pitch = 0.0f; + value->fire = 0; + value->alt_fire = 0; + value->boost = 0; + value->brake = 0; + value->aim = 0; + value->lock_on = 0; + value->zoom = 0; + value->ping = 0; +} + +static SCHEMA_UNUSED void schema_example_input_reset_raw_( void * storage ) { input_reset( (Input *) storage ); } + +extern const TableTypeInfo schema_example_input_info_; +static SCHEMA_UNUSED const TableTypeInfo * input_table_type(void) { return &schema_example_input_info_; } +static SCHEMA_UNUSED void input_packet_reset( InputPacket * value ) +{ + value->synchronize_sequence = 0; + value->current_frame = 0; + value->start_frame = 0; + input_reset( &value->inputs[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 16 ); i++ ) { value->inputs[i] = value->inputs[0]; } } + value->inputs_count = 0; +} + +static SCHEMA_UNUSED void schema_example_input_packet_reset_raw_( void * storage ) { input_packet_reset( (InputPacket *) storage ); } + +extern const TableTypeInfo schema_example_input_packet_info_; +static SCHEMA_UNUSED const TableTypeInfo * input_packet_table_type(void) { return &schema_example_input_packet_info_; } +static SCHEMA_UNUSED void narrow_reset( Narrow * value ) +{ + value->n = 0; +} + +static SCHEMA_UNUSED void schema_example_narrow_reset_raw_( void * storage ) { narrow_reset( (Narrow *) storage ); } + +extern const TableTypeInfo schema_example_narrow_info_; +static SCHEMA_UNUSED const TableTypeInfo * narrow_table_type(void) { return &schema_example_narrow_info_; } +static SCHEMA_UNUSED void probe_array_reset( ProbeArray * value ) +{ + probe_sample_reset( &value->samples[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 2 ); i++ ) { value->samples[i] = value->samples[0]; } } + probe_config_reset( &value->config ); +} + +static SCHEMA_UNUSED void schema_example_probe_array_reset_raw_( void * storage ) { probe_array_reset( (ProbeArray *) storage ); } + +extern const TableTypeInfo schema_example_probe_array_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_array_table_type(void) { return &schema_example_probe_array_info_; } +static SCHEMA_UNUSED void probe_bits_reset( ProbeBits * value ) +{ + value->small = 0; + value->boundary = 0; + value->wide = 0; + value->sensor = 0; + value->nonce = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_bits_reset_raw_( void * storage ) { probe_bits_reset( (ProbeBits *) storage ); } + +extern const TableTypeInfo schema_example_probe_bits_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_bits_table_type(void) { return &schema_example_probe_bits_info_; } +static SCHEMA_UNUSED void probe_collider_reset( ProbeCollider * value ) +{ + value->armor = 0; + memset( &value->shape, 0, sizeof( value->shape ) ); + memset( &value->backup, 0, sizeof( value->backup ) ); + memset( &value->extras[0], 0, sizeof( value->extras[0] ) ); + { int32_t i; for ( i = 1; i < (int32_t) ( 2 ); i++ ) { value->extras[i] = value->extras[0]; } } + value->extras_count = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_collider_reset_raw_( void * storage ) { probe_collider_reset( (ProbeCollider *) storage ); } + +extern const TableTypeInfo schema_example_probe_collider_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_collider_table_type(void) { return &schema_example_probe_collider_info_; } +static SCHEMA_UNUSED void probe_config_reset( ProbeConfig * value ) +{ + value->retries = -1; + value->preferred = WEAPON_RAILGUN; +} + +static SCHEMA_UNUSED void schema_example_probe_config_reset_raw_( void * storage ) { probe_config_reset( (ProbeConfig *) storage ); } + +extern const TableTypeInfo schema_example_probe_config_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_config_table_type(void) { return &schema_example_probe_config_info_; } +static SCHEMA_UNUSED void probe_header_reset( ProbeHeader * value ) +{ + value->version = 0; + value->probe_id = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_header_reset_raw_( void * storage ) { probe_header_reset( (ProbeHeader *) storage ); } + +extern const TableTypeInfo schema_example_probe_header_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_header_table_type(void) { return &schema_example_probe_header_info_; } +static SCHEMA_UNUSED void probe_report_reset( ProbeReport * value ) +{ + probe_header_reset( &value->header ); + value->flags = 0; + test_reset( &value->echo ); +} + +static SCHEMA_UNUSED void schema_example_probe_report_reset_raw_( void * storage ) { probe_report_reset( (ProbeReport *) storage ); } + +extern const TableTypeInfo schema_example_probe_report_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_report_table_type(void) { return &schema_example_probe_report_info_; } +static SCHEMA_UNUSED void probe_ring_reset( ProbeRing * value ) +{ + value->radius = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_ring_reset_raw_( void * storage ) { probe_ring_reset( (ProbeRing *) storage ); } + +extern const TableTypeInfo schema_example_probe_ring_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_ring_table_type(void) { return &schema_example_probe_ring_info_; } +static SCHEMA_UNUSED void probe_sample_reset( ProbeSample * value ) +{ + value->active = 1; + value->orientation = 0.0f; + value->raw_delta = 0; + value->big_delta = 0; + value->weapon = WEAPON_NONE; + value->has_target = 0; + value->target_id = 0; + value->idle_ticks = 0; + memset( value->samples, 0, sizeof( value->samples ) ); + value->samples_count = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_sample_reset_raw_( void * storage ) { probe_sample_reset( (ProbeSample *) storage ); } + +extern const TableTypeInfo schema_example_probe_sample_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_sample_table_type(void) { return &schema_example_probe_sample_info_; } +static SCHEMA_UNUSED void probe_slab_reset( ProbeSlab * value ) +{ + value->width = 0; + value->height = 0; +} + +static SCHEMA_UNUSED void schema_example_probe_slab_reset_raw_( void * storage ) { probe_slab_reset( (ProbeSlab *) storage ); } + +extern const TableTypeInfo schema_example_probe_slab_info_; +static SCHEMA_UNUSED const TableTypeInfo * probe_slab_table_type(void) { return &schema_example_probe_slab_info_; } +static SCHEMA_UNUSED void quantized_position_reset( QuantizedPosition * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; +} + +static SCHEMA_UNUSED void schema_example_quantized_position_reset_raw_( void * storage ) { quantized_position_reset( (QuantizedPosition *) storage ); } + +extern const TableTypeInfo schema_example_quantized_position_info_; +static SCHEMA_UNUSED const TableTypeInfo * quantized_position_table_type(void) { return &schema_example_quantized_position_info_; } +static SCHEMA_UNUSED void quantized_rotation_reset( QuantizedRotation * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; + value->w = 0; +} + +static SCHEMA_UNUSED void schema_example_quantized_rotation_reset_raw_( void * storage ) { quantized_rotation_reset( (QuantizedRotation *) storage ); } + +extern const TableTypeInfo schema_example_quantized_rotation_info_; +static SCHEMA_UNUSED const TableTypeInfo * quantized_rotation_table_type(void) { return &schema_example_quantized_rotation_info_; } +static SCHEMA_UNUSED void quantized_velocity_reset( QuantizedVelocity * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; +} + +static SCHEMA_UNUSED void schema_example_quantized_velocity_reset_raw_( void * storage ) { quantized_velocity_reset( (QuantizedVelocity *) storage ); } + +extern const TableTypeInfo schema_example_quantized_velocity_info_; +static SCHEMA_UNUSED const TableTypeInfo * quantized_velocity_table_type(void) { return &schema_example_quantized_velocity_info_; } +static SCHEMA_UNUSED void quat_reset( Quat * value ) +{ + value->x = 0.0; + value->y = 0.0; + value->z = 0.0; + value->w = 0.0; +} + +static SCHEMA_UNUSED void schema_example_quat_reset_raw_( void * storage ) { quat_reset( (Quat *) storage ); } + +extern const TableTypeInfo schema_example_quat_info_; +static SCHEMA_UNUSED const TableTypeInfo * quat_table_type(void) { return &schema_example_quat_info_; } +static SCHEMA_UNUSED void regain_after_align_reset( RegainAfterAlign * value ) +{ + value->lead = 0; + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; + memset( value->s, 0, sizeof( value->s ) ); + value->s_length = 0; + value->p = 0; + value->q = 0; + value->r = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_regain_after_align_reset_raw_( void * storage ) { regain_after_align_reset( (RegainAfterAlign *) storage ); } + +extern const TableTypeInfo schema_example_regain_after_align_info_; +static SCHEMA_UNUSED const TableTypeInfo * regain_after_align_table_type(void) { return &schema_example_regain_after_align_info_; } +static SCHEMA_UNUSED void render_block_reset( RenderBlock * value ) +{ + value->worker_index = 0; + value->sprite_count_hint = 0; + render_sprite_reset( &value->sprites[0] ); + { int32_t i; for ( i = 1; i < (int32_t) ( 64 ); i++ ) { value->sprites[i] = value->sprites[0]; } } + value->sprites_count = 0; +} + +static SCHEMA_UNUSED void schema_example_render_block_reset_raw_( void * storage ) { render_block_reset( (RenderBlock *) storage ); } + +extern const TableTypeInfo schema_example_render_block_info_; +static SCHEMA_UNUSED const TableTypeInfo * render_block_table_type(void) { return &schema_example_render_block_info_; } +static SCHEMA_UNUSED void render_sprite_reset( RenderSprite * value ) +{ + value->sort_key = 0; + value->mesh_id = 0; + value->material_id = 0; + value->layer = 0; + value->team = TEAM_NONE; +} + +static SCHEMA_UNUSED void schema_example_render_sprite_reset_raw_( void * storage ) { render_sprite_reset( (RenderSprite *) storage ); } + +extern const TableTypeInfo schema_example_render_sprite_info_; +static SCHEMA_UNUSED const TableTypeInfo * render_sprite_table_type(void) { return &schema_example_render_sprite_info_; } +static SCHEMA_UNUSED void rigid_body_reset( RigidBody * value ) +{ + vec3_reset( &value->position ); + quat_reset( &value->orientation ); + value->at_rest = 0; + vec3_reset( &value->linear_velocity ); + vec3_reset( &value->angular_velocity ); +} + +static SCHEMA_UNUSED void schema_example_rigid_body_reset_raw_( void * storage ) { rigid_body_reset( (RigidBody *) storage ); } + +extern const TableTypeInfo schema_example_rigid_body_info_; +static SCHEMA_UNUSED const TableTypeInfo * rigid_body_table_type(void) { return &schema_example_rigid_body_info_; } +static SCHEMA_UNUSED void ship_create_reset( ShipCreate * value ) +{ + value->ship_type = SHIP_TYPE_NONE; + quantized_position_reset( &value->position ); + quantized_rotation_reset( &value->rotation ); + quantized_velocity_reset( &value->linear_velocity ); + value->has_flags = 0; + value->flags = 0; + value->team = TEAM_NONE; + value->health = 0; + value->thrust = 0; + value->pending = PENDING_NONE; +} + +static SCHEMA_UNUSED void schema_example_ship_create_reset_raw_( void * storage ) { ship_create_reset( (ShipCreate *) storage ); } + +extern const TableTypeInfo schema_example_ship_create_info_; +static SCHEMA_UNUSED const TableTypeInfo * ship_create_table_type(void) { return &schema_example_ship_create_info_; } +static SCHEMA_UNUSED void sole_reset( Sole * value ) +{ + value->only = 0; +} + +static SCHEMA_UNUSED void schema_example_sole_reset_raw_( void * storage ) { sole_reset( (Sole *) storage ); } + +extern const TableTypeInfo schema_example_sole_info_; +static SCHEMA_UNUSED const TableTypeInfo * sole_table_type(void) { return &schema_example_sole_info_; } +static SCHEMA_UNUSED void span_chunk_reset( SpanChunk * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_chunk_reset_raw_( void * storage ) { span_chunk_reset( (SpanChunk *) storage ); } + +extern const TableTypeInfo schema_example_span_chunk_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_chunk_table_type(void) { return &schema_example_span_chunk_info_; } +static SCHEMA_UNUSED void span_f64_reset( SpanF64 * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_f64_reset_raw_( void * storage ) { span_f64_reset( (SpanF64 *) storage ); } + +extern const TableTypeInfo schema_example_span_f64_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_f64_table_type(void) { return &schema_example_span_f64_info_; } +static SCHEMA_UNUSED void span_i64_reset( SpanI64 * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_i64_reset_raw_( void * storage ) { span_i64_reset( (SpanI64 *) storage ); } + +extern const TableTypeInfo schema_example_span_i64_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_i64_table_type(void) { return &schema_example_span_i64_info_; } +static SCHEMA_UNUSED void span_one_reset( SpanOne * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_one_reset_raw_( void * storage ) { span_one_reset( (SpanOne *) storage ); } + +extern const TableTypeInfo schema_example_span_one_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_one_table_type(void) { return &schema_example_span_one_info_; } +static SCHEMA_UNUSED void span_tail_reset( SpanTail * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_span_tail_reset_raw_( void * storage ) { span_tail_reset( (SpanTail *) storage ); } + +extern const TableTypeInfo schema_example_span_tail_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_tail_table_type(void) { return &schema_example_span_tail_info_; } +static SCHEMA_UNUSED void span_twice_reset( SpanTwice * value ) +{ + memset( value->a, 0, sizeof( value->a ) ); + memset( value->b, 0, sizeof( value->b ) ); +} + +static SCHEMA_UNUSED void schema_example_span_twice_reset_raw_( void * storage ) { span_twice_reset( (SpanTwice *) storage ); } + +extern const TableTypeInfo schema_example_span_twice_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_twice_table_type(void) { return &schema_example_span_twice_info_; } +static SCHEMA_UNUSED void span_u64_reset( SpanU64 * value ) +{ + memset( value->values, 0, sizeof( value->values ) ); +} + +static SCHEMA_UNUSED void schema_example_span_u64_reset_raw_( void * storage ) { span_u64_reset( (SpanU64 *) storage ); } + +extern const TableTypeInfo schema_example_span_u64_info_; +static SCHEMA_UNUSED const TableTypeInfo * span_u64_table_type(void) { return &schema_example_span_u64_info_; } +static SCHEMA_UNUSED void strs_reset( Strs * value ) +{ + value->lead = 0; + memset( value->s, 0, sizeof( value->s ) ); + value->s_length = 0; + memset( value->b, 0, sizeof( value->b ) ); + value->b_length = 0; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_example_strs_reset_raw_( void * storage ) { strs_reset( (Strs *) storage ); } + +extern const TableTypeInfo schema_example_strs_info_; +static SCHEMA_UNUSED const TableTypeInfo * strs_table_type(void) { return &schema_example_strs_info_; } +static SCHEMA_UNUSED void test_reset( Test * value ) +{ + value->test_a = 0; + value->test_b = 0; + value->test_c = 0; + value->test_d = 0; +} + +static SCHEMA_UNUSED void schema_example_test_reset_raw_( void * storage ) { test_reset( (Test *) storage ); } + +extern const TableTypeInfo schema_example_test_info_; +static SCHEMA_UNUSED const TableTypeInfo * test_table_type(void) { return &schema_example_test_info_; } +static SCHEMA_UNUSED void test_data_reset( TestData * value ) +{ + value->a = 0; + value->b = 0; + value->c = 0; + value->d = 0; + value->e = 0; + value->f = 0; + value->g = 0; + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; + value->float_value = 0.0f; + value->compressed_float_value = 0.0f; + value->double_value = 0.0; + value->int8_value = 0; + value->int16_value = 0; + value->uint8_value = 0; + value->uint16_value = 0; + value->uint32_value = 0; + value->uint64_value = 0; + value->int64_full = 0; + value->int64_range = 0; + memset( value->fixed_bytes, 0, sizeof( value->fixed_bytes ) ); + memset( value->text, 0, sizeof( value->text ) ); + value->text_length = 0; +} + +static SCHEMA_UNUSED void schema_example_test_data_reset_raw_( void * storage ) { test_data_reset( (TestData *) storage ); } + +extern const TableTypeInfo schema_example_test_data_info_; +static SCHEMA_UNUSED const TableTypeInfo * test_data_table_type(void) { return &schema_example_test_data_info_; } +static SCHEMA_UNUSED void tri3_reset( Tri3 * value ) +{ + value->a = 0; + value->b = 0; +} + +static SCHEMA_UNUSED void schema_example_tri3_reset_raw_( void * storage ) { tri3_reset( (Tri3 *) storage ); } + +extern const TableTypeInfo schema_example_tri3_info_; +static SCHEMA_UNUSED const TableTypeInfo * tri3_table_type(void) { return &schema_example_tri3_info_; } +static SCHEMA_UNUSED void trio_reset( Trio * value ) +{ + value->a = 0; + value->b = 0; + value->c = 0; +} + +static SCHEMA_UNUSED void schema_example_trio_reset_raw_( void * storage ) { trio_reset( (Trio *) storage ); } + +extern const TableTypeInfo schema_example_trio_info_; +static SCHEMA_UNUSED const TableTypeInfo * trio_table_type(void) { return &schema_example_trio_info_; } +static SCHEMA_UNUSED void trio_first_reset( TrioFirst * value ) +{ + trio_reset( &value->inner ); + value->trailer = 0; +} + +static SCHEMA_UNUSED void schema_example_trio_first_reset_raw_( void * storage ) { trio_first_reset( (TrioFirst *) storage ); } + +extern const TableTypeInfo schema_example_trio_first_info_; +static SCHEMA_UNUSED const TableTypeInfo * trio_first_table_type(void) { return &schema_example_trio_first_info_; } +static SCHEMA_UNUSED void trio_sole_reset( TrioSole * value ) +{ + trio_reset( &value->inner ); +} + +static SCHEMA_UNUSED void schema_example_trio_sole_reset_raw_( void * storage ) { trio_sole_reset( (TrioSole *) storage ); } + +extern const TableTypeInfo schema_example_trio_sole_info_; +static SCHEMA_UNUSED const TableTypeInfo * trio_sole_table_type(void) { return &schema_example_trio_sole_info_; } +static SCHEMA_UNUSED void trio_straddle_reset( TrioStraddle * value ) +{ + value->pad0 = 0; + value->pad1 = 0; + value->pad2 = 0; + value->pad3 = 0; + value->pad4 = 0; + value->pad5 = 0; + trio_reset( &value->inner ); +} + +static SCHEMA_UNUSED void schema_example_trio_straddle_reset_raw_( void * storage ) { trio_straddle_reset( (TrioStraddle *) storage ); } + +extern const TableTypeInfo schema_example_trio_straddle_info_; +static SCHEMA_UNUSED const TableTypeInfo * trio_straddle_table_type(void) { return &schema_example_trio_straddle_info_; } +static SCHEMA_UNUSED void vec2_reset( Vec2 * value ) +{ + value->x = 0.0; + value->y = 0.0; +} + +static SCHEMA_UNUSED void schema_example_vec2_reset_raw_( void * storage ) { vec2_reset( (Vec2 *) storage ); } + +extern const TableTypeInfo schema_example_vec2_info_; +static SCHEMA_UNUSED const TableTypeInfo * vec2_table_type(void) { return &schema_example_vec2_info_; } +static SCHEMA_UNUSED void vec3_reset( Vec3 * value ) +{ + value->x = 0.0; + value->y = 0.0; + value->z = 0.0; +} + +static SCHEMA_UNUSED void schema_example_vec3_reset_raw_( void * storage ) { vec3_reset( (Vec3 *) storage ); } + +extern const TableTypeInfo schema_example_vec3_info_; +static SCHEMA_UNUSED const TableTypeInfo * vec3_table_type(void) { return &schema_example_vec3_info_; } +static SCHEMA_UNUSED void w1_reset( W1 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w1_reset_raw_( void * storage ) { w1_reset( (W1 *) storage ); } + +extern const TableTypeInfo schema_example_w1_info_; +static SCHEMA_UNUSED const TableTypeInfo * w1_table_type(void) { return &schema_example_w1_info_; } +static SCHEMA_UNUSED void w13_reset( W13 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w13_reset_raw_( void * storage ) { w13_reset( (W13 *) storage ); } + +extern const TableTypeInfo schema_example_w13_info_; +static SCHEMA_UNUSED const TableTypeInfo * w13_table_type(void) { return &schema_example_w13_info_; } +static SCHEMA_UNUSED void w17_reset( W17 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w17_reset_raw_( void * storage ) { w17_reset( (W17 *) storage ); } + +extern const TableTypeInfo schema_example_w17_info_; +static SCHEMA_UNUSED const TableTypeInfo * w17_table_type(void) { return &schema_example_w17_info_; } +static SCHEMA_UNUSED void w26_reset( W26 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w26_reset_raw_( void * storage ) { w26_reset( (W26 *) storage ); } + +extern const TableTypeInfo schema_example_w26_info_; +static SCHEMA_UNUSED const TableTypeInfo * w26_table_type(void) { return &schema_example_w26_info_; } +static SCHEMA_UNUSED void w50_reset( W50 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w50_reset_raw_( void * storage ) { w50_reset( (W50 *) storage ); } + +extern const TableTypeInfo schema_example_w50_info_; +static SCHEMA_UNUSED const TableTypeInfo * w50_table_type(void) { return &schema_example_w50_info_; } +static SCHEMA_UNUSED void w52_reset( W52 * value ) +{ + memset( value->items, 0, sizeof( value->items ) ); + value->items_count = 0; +} + +static SCHEMA_UNUSED void schema_example_w52_reset_raw_( void * storage ) { w52_reset( (W52 *) storage ); } + +extern const TableTypeInfo schema_example_w52_info_; +static SCHEMA_UNUSED const TableTypeInfo * w52_table_type(void) { return &schema_example_w52_info_; } +static SCHEMA_UNUSED void wide_reset( Wide * value ) +{ + value->w = 0; +} + +static SCHEMA_UNUSED void schema_example_wide_reset_raw_( void * storage ) { wide_reset( (Wide *) storage ); } + +extern const TableTypeInfo schema_example_wide_info_; +static SCHEMA_UNUSED const TableTypeInfo * wide_table_type(void) { return &schema_example_wide_info_; } +const UnitViewInfo * schema_example_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_example_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/testdata/golden/ludicrous/c/LudicrousView.c b/testdata/golden/ludicrous/c/LudicrousView.c new file mode 100644 index 000000000..9915745a4 --- /dev/null +++ b/testdata/golden/ludicrous/c/LudicrousView.c @@ -0,0 +1,137 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "LudicrousView.h" +static const TableWideRange schema_ludicrous_degenerate_probe_locked_fixed_outside_range_ = { { 18446744073709355008ull, 18446744073709551615ull }, { 18446744073709355008ull, 18446744073709551615ull } }; +static const TableWideRange schema_ludicrous_degenerate_probe_locked_wide_outside_range_ = { { 18446731728030650382ull, 18446744073709551615ull }, { 18446731728030650382ull, 18446744073709551615ull } }; +static const TableFieldInfo schema_ludicrous_degenerate_probe_fields_[] = { + { "locked_fixed", NULL, "fixed(16, 16)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( DegenerateProbe, locked_fixed ), (uint32_t) sizeof( ( (DegenerateProbe *) 0 )->locked_fixed ), 0xffffffffu, 0xffffffffu, NULL, 1, -3.0, -3.0, &schema_ludicrous_degenerate_probe_locked_fixed_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "locked_int", NULL, "int32", 0x0000000000000000ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( DegenerateProbe, locked_int ), (uint32_t) sizeof( ( (DegenerateProbe *) 0 )->locked_int ), 0xffffffffu, 0xffffffffu, NULL, 1, 7.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "locked_wide", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( DegenerateProbe, locked_wide ), (uint32_t) sizeof( ( (DegenerateProbe *) 0 )->locked_wide ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.2345678901234e+13, -1.2345678901234e+13, &schema_ludicrous_degenerate_probe_locked_wide_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( DegenerateProbe, tail ), (uint32_t) sizeof( ( (DegenerateProbe *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_degenerate_probe_info_ = { "DegenerateProbe", (uint32_t) sizeof( DegenerateProbe ), 4, schema_ludicrous_degenerate_probe_fields_, schema_ludicrous_degenerate_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_fixed_probe_angle_outside_range_ = { { 18446744073697755136ull, 18446744073709551615ull }, { 11796480ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_probe_position_outside_range_ = { { 18446744071743471616ull, 18446744073709551615ull }, { 1966080000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_probe_reach_outside_range_ = { { 18446744008173551616ull, 18446744073709551615ull }, { 65536000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_probe_ticks_outside_range_ = { { 0ull, 0ull }, { 1000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_probe_samples_outside_range_ = { { 18446744073709027328ull, 18446744073709551615ull }, { 524288ull, 0ull } }; +static const TableFieldInfo schema_ludicrous_fixed_probe_fields_[] = { + { "angle", NULL, "fixed(16, 16)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedProbe, angle ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->angle ), 0xffffffffu, 0xffffffffu, NULL, 1, -180.0, 180.0, &schema_ludicrous_fixed_probe_angle_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "position", NULL, "fixed(48, 16)", 0x0000000000000000ull, 23, 0, 0, 0, 0, (uint32_t) offsetof( FixedProbe, position ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->position ), 0xffffffffu, 0xffffffffu, NULL, 1, -30000.0, 30000.0, &schema_ludicrous_fixed_probe_position_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "reach", NULL, "fixed(112, 16)", 0x0000000000000000ull, 24, 0, 0, 0, 0, (uint32_t) offsetof( FixedProbe, reach ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->reach ), 0xffffffffu, 0xffffffffu, NULL, 1, -1e+06, 1e+06, &schema_ludicrous_fixed_probe_reach_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ticks", NULL, "fixed(32, 0)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedProbe, ticks ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->ticks ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1e+06, &schema_ludicrous_fixed_probe_ticks_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "samples", NULL, "fixed(16, 16)", 0x0000000000000000ull, 22, 1, 0, 0, 2, (uint32_t) offsetof( FixedProbe, samples ), (uint32_t) sizeof( ( (FixedProbe *) 0 )->samples[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, -8.0, 8.0, &schema_ludicrous_fixed_probe_samples_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_fixed_probe_info_ = { "FixedProbe", (uint32_t) sizeof( FixedProbe ), 5, schema_ludicrous_fixed_probe_fields_, schema_ludicrous_fixed_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_fixed_quat_x_outside_range_ = { { 18446744072635809792ull, 18446744073709551615ull }, { 1073741824ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_quat_y_outside_range_ = { { 18446744072635809792ull, 18446744073709551615ull }, { 1073741824ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_quat_z_outside_range_ = { { 18446744072635809792ull, 18446744073709551615ull }, { 1073741824ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_quat_w_outside_range_ = { { 18446744072635809792ull, 18446744073709551615ull }, { 1073741824ull, 0ull } }; +static const TableFieldInfo schema_ludicrous_fixed_quat_fields_[] = { + { "x", NULL, "fixed(2, 30)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedQuat, x ), (uint32_t) sizeof( ( (FixedQuat *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, &schema_ludicrous_fixed_quat_x_outside_range_, 30, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "fixed(2, 30)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedQuat, y ), (uint32_t) sizeof( ( (FixedQuat *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, &schema_ludicrous_fixed_quat_y_outside_range_, 30, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "fixed(2, 30)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedQuat, z ), (uint32_t) sizeof( ( (FixedQuat *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, &schema_ludicrous_fixed_quat_z_outside_range_, 30, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "w", NULL, "fixed(2, 30)", 0x0000000000000000ull, 22, 0, 0, 0, 0, (uint32_t) offsetof( FixedQuat, w ), (uint32_t) sizeof( ( (FixedQuat *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.0, 1.0, &schema_ludicrous_fixed_quat_w_outside_range_, 30, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_fixed_quat_info_ = { "FixedQuat", (uint32_t) sizeof( FixedQuat ), 4, schema_ludicrous_fixed_quat_fields_, schema_ludicrous_fixed_quat_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_fixed_vec_x_outside_range_ = { { 18446744067155951616ull, 18446744073709551615ull }, { 6553600000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_vec_y_outside_range_ = { { 18446744067155951616ull, 18446744073709551615ull }, { 6553600000ull, 0ull } }; +static const TableWideRange schema_ludicrous_fixed_vec_z_outside_range_ = { { 18446744067155951616ull, 18446744073709551615ull }, { 6553600000ull, 0ull } }; +static const TableFieldInfo schema_ludicrous_fixed_vec_fields_[] = { + { "x", NULL, "fixed(48, 16)", 0x0000000000000000ull, 23, 0, 0, 0, 0, (uint32_t) offsetof( FixedVec, x ), (uint32_t) sizeof( ( (FixedVec *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 1, -100000.0, 100000.0, &schema_ludicrous_fixed_vec_x_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", NULL, "fixed(48, 16)", 0x0000000000000000ull, 23, 0, 0, 0, 0, (uint32_t) offsetof( FixedVec, y ), (uint32_t) sizeof( ( (FixedVec *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 1, -100000.0, 100000.0, &schema_ludicrous_fixed_vec_y_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", NULL, "fixed(48, 16)", 0x0000000000000000ull, 23, 0, 0, 0, 0, (uint32_t) offsetof( FixedVec, z ), (uint32_t) sizeof( ( (FixedVec *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 1, -100000.0, 100000.0, &schema_ludicrous_fixed_vec_z_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_fixed_vec_info_ = { "FixedVec", (uint32_t) sizeof( FixedVec ), 3, schema_ludicrous_fixed_vec_fields_, schema_ludicrous_fixed_vec_reset_raw_, TableDocNone, 0, NULL }; + +static const TableVariantInfo schema_ludicrous_ludicrous_state_mode_outside_variants_[] = { + { "None", 0x0000000000000000ull }, + { "Cruise", 0x0000000000000000ull }, + { "Warp", 0x0000000000000000ull }, + { "Ludicrous", 0x0000000000000000ull }, +}; +static const TableWideRange schema_ludicrous_ludicrous_state_keys_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableWideRange schema_ludicrous_ludicrous_state_target_id_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableFieldInfo schema_ludicrous_ludicrous_state_fields_[] = { + { "mode", NULL, "DriveMode", 0x0000000000000000ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, mode ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->mode ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_ludicrous_ludicrous_state_mode_outside_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "probe", NULL, "FixedProbe", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, probe ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->probe ), 0xffffffffu, 0xffffffffu, &schema_ludicrous_fixed_probe_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "wide", NULL, "WideProbe", 0x0000000000000000ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, wide ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->wide ), 0xffffffffu, 0xffffffffu, &schema_ludicrous_wide_probe_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "keys", NULL, "uint128", 0x0000000000000000ull, 19, 1, 1, 0, 4, (uint32_t) offsetof( LudicrousState, keys ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->keys[0] ), (uint32_t) offsetof( LudicrousState, keys_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_ludicrous_ludicrous_state_keys_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_target", NULL, "bool", 0x0000000000000000ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, has_target ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->has_target ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "target_id", NULL, "uint128", 0x0000000000000000ull, 19, 0, 0, 0, 0, (uint32_t) offsetof( LudicrousState, target_id ), (uint32_t) sizeof( ( (LudicrousState *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_ludicrous_ludicrous_state_target_id_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "has_target", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_ludicrous_state_info_ = { "LudicrousState", (uint32_t) sizeof( LudicrousState ), 6, schema_ludicrous_ludicrous_state_fields_, schema_ludicrous_ludicrous_state_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_unsigned_probe_angle_outside_range_ = { { 0ull, 0ull }, { 23592960ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_span_outside_range_ = { { 0ull, 0ull }, { 18446744073709486080ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_reach_outside_range_ = { { 0ull, 0ull }, { 131072000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_ticks_outside_range_ = { { 0ull, 0ull }, { 1000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_samples_outside_range_ = { { 0ull, 0ull }, { 1048576ull, 0ull } }; +static const TableWideRange schema_ludicrous_unsigned_probe_locked_outside_range_ = { { 196608ull, 0ull }, { 196608ull, 0ull } }; +static const TableFieldInfo schema_ludicrous_unsigned_probe_fields_[] = { + { "angle", NULL, "ufixed(16, 16)", 0x0000000000000000ull, 27, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, angle ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->angle ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 360.0, &schema_ludicrous_unsigned_probe_angle_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "span", NULL, "ufixed(48, 16)", 0x0000000000000000ull, 28, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, span ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, &schema_ludicrous_unsigned_probe_span_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "reach", NULL, "ufixed(112, 16)", 0x0000000000000000ull, 29, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, reach ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->reach ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2e+06, &schema_ludicrous_unsigned_probe_reach_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ticks", NULL, "ufixed(32, 0)", 0x0000000000000000ull, 27, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, ticks ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->ticks ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1e+06, &schema_ludicrous_unsigned_probe_ticks_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "samples", NULL, "ufixed(16, 16)", 0x0000000000000000ull, 27, 1, 0, 0, 2, (uint32_t) offsetof( UnsignedProbe, samples ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->samples[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 16.0, &schema_ludicrous_unsigned_probe_samples_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "locked", NULL, "ufixed(16, 16)", 0x0000000000000000ull, 27, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, locked ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->locked ), 0xffffffffu, 0xffffffffu, NULL, 1, 3.0, 3.0, &schema_ludicrous_unsigned_probe_locked_outside_range_, 16, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tail", NULL, "uint8", 0x0000000000000000ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( UnsignedProbe, tail ), (uint32_t) sizeof( ( (UnsignedProbe *) 0 )->tail ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_unsigned_probe_info_ = { "UnsignedProbe", (uint32_t) sizeof( UnsignedProbe ), 7, schema_ludicrous_unsigned_probe_fields_, schema_ludicrous_unsigned_probe_reset_raw_, TableDocNone, 0, NULL }; + +static const TableWideRange schema_ludicrous_wide_probe_entity_id_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableWideRange schema_ludicrous_wide_probe_energy_outside_range_ = { { 18446744068709551616ull, 18446744073709551615ull }, { 5000000000ull, 0ull } }; +static const TableWideRange schema_ludicrous_wide_probe_flux_outside_range_ = { { 0ull, 18446744004990074880ull }, { 0ull, 68719476736ull } }; +static const TableWideRange schema_ludicrous_wide_probe_bias_outside_range_ = { { 18446744073709550616ull, 18446744073709551615ull }, { 1000ull, 0ull } }; +static const TableWideRange schema_ludicrous_wide_probe_seed_outside_range_ = { { 0ull, 0ull }, { 18446744073709551615ull, 18446744073709551615ull } }; +static const TableFieldInfo schema_ludicrous_wide_probe_fields_[] = { + { "entity_id", NULL, "uint128", 0x0000000000000000ull, 19, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, entity_id ), (uint32_t) sizeof( ( (WideProbe *) 0 )->entity_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_ludicrous_wide_probe_entity_id_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "energy", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, energy ), (uint32_t) sizeof( ( (WideProbe *) 0 )->energy ), 0xffffffffu, 0xffffffffu, NULL, 1, -5e+09, 5e+09, &schema_ludicrous_wide_probe_energy_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flux", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, flux ), (uint32_t) sizeof( ( (WideProbe *) 0 )->flux ), 0xffffffffu, 0xffffffffu, NULL, 1, -1.2676506002282294e+30, 1.2676506002282294e+30, &schema_ludicrous_wide_probe_flux_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "bias", NULL, "int128", 0x0000000000000000ull, 18, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, bias ), (uint32_t) sizeof( ( (WideProbe *) 0 )->bias ), 0xffffffffu, 0xffffffffu, NULL, 1, -1000.0, 1000.0, &schema_ludicrous_wide_probe_bias_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "seed", NULL, "uint128", 0x0000000000000000ull, 19, 0, 0, 0, 0, (uint32_t) offsetof( WideProbe, seed ), (uint32_t) sizeof( ( (WideProbe *) 0 )->seed ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, &schema_ludicrous_wide_probe_seed_outside_range_, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_ludicrous_wide_probe_info_ = { "WideProbe", (uint32_t) sizeof( WideProbe ), 5, schema_ludicrous_wide_probe_fields_, schema_ludicrous_wide_probe_reset_raw_, TableDocNone, 0, NULL }; + +const UnitViewInfo * schema_ludicrous_unit_view_(void) +{ + static const ViewVariant DriveMode_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Cruise", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Warp", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Ludicrous", 0x0000000000000000ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "DriveMode", "Ludicrous.schema", 3, 8, 4, DriveMode_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "DegenerateProbe", "Ludicrous.schema", 0, &schema_ludicrous_degenerate_probe_info_, TableDocNone, 0, NULL }, + { "FixedProbe", "Ludicrous.schema", 0, &schema_ludicrous_fixed_probe_info_, TableDocNone, 0, NULL }, + { "FixedQuat", "Ludicrous.schema", 0, &schema_ludicrous_fixed_quat_info_, TableDocNone, 0, NULL }, + { "FixedVec", "Ludicrous.schema", 0, &schema_ludicrous_fixed_vec_info_, TableDocNone, 0, NULL }, + { "LudicrousState", "Ludicrous.schema", 0, &schema_ludicrous_ludicrous_state_info_, TableDocNone, 0, NULL }, + { "UnsignedProbe", "Ludicrous.schema", 0, &schema_ludicrous_unsigned_probe_info_, TableDocNone, 0, NULL }, + { "WideProbe", "Ludicrous.schema", 0, &schema_ludicrous_wide_probe_info_, TableDocNone, 0, NULL }, + }; + static const ViewConstant constants[] = { + { "MaxWorldUnits", "Ludicrous.schema", "int64", 0, INT64_C(30000), 0.0, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "ludicrous", 0xf4a226f4166d919bull, + 7, types, + 0, NULL, + 1, enums, + 0, NULL, + 0, NULL, + 1, constants }; + return &info; +} diff --git a/testdata/golden/ludicrous/c/LudicrousView.h b/testdata/golden/ludicrous/c/LudicrousView.h new file mode 100644 index 000000000..14c44bc81 --- /dev/null +++ b/testdata/golden/ludicrous/c/LudicrousView.h @@ -0,0 +1,431 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_LUDICROUS_VIEW_H +#define SCHEMA_LUDICROUS_VIEW_H +#include +#include +#include +#include "Ludicrous.h" +#ifndef SCHEMA_LUDICROUS_TABLE_PRIMITIVES +#define SCHEMA_LUDICROUS_TABLE_PRIMITIVES + +/* THE CODEC DOES NOT DEPEND ON THE COMPILER'S INLINING BUDGET. A table of a + realistic field count emits one large body per type, and the cursor a body + writes through lives in the caller's TableWriter: across a call boundary that + cursor round-trips through memory, and a uint8_t * store may alias the writer + itself, so every put reloads it. When a budget runs out mid-body the codec + silently degrades to that shape. Forcing the primitives and the fixed-class + bodies inline is what keeps the cursor in registers and lets adjacent + constant framing bytes merge into one store. + + The spelling is FEATURE TESTED rather than assumed, exactly as the packet + emitter's own inlining demand is (SPEC §6.1's C column). + + IT CARRIES THE PACKAGE for the reason every other name here does, and that + reason is the LINKER rather than the preprocessor: two units of the same + schema family link into one program — two generations of one schema is the + case the conformance driver itself is — so nothing this backend emits may + be spelled the same way by two packages. They do NOT share a translation + unit and cannot: C has no namespace, so two units' headers in one TU + redefine TableReport, TableWriter and every other runtime name, which is the + packet emitter's standing limit (SPEC §6.1) and not something tables + changed. A per-package macro is what keeps the LINK legal. */ +#ifndef SCHEMA_LUDICROUS_TABLE_INLINE +#if defined( _MSC_VER ) +#define SCHEMA_LUDICROUS_TABLE_INLINE __forceinline +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_LUDICROUS_TABLE_INLINE inline __attribute__(( always_inline )) +#else +#define SCHEMA_LUDICROUS_TABLE_INLINE inline +#endif +#endif + +/* A COMPILE-TIME assertion under -std=c99, which has none of its own. C11's + _Static_assert carries the message into the build log verbatim and is used + wherever the compiler has it; everywhere else the negative array bound is + the same refusal with a tag a reader can find. The tag is unique per record + and field, and each record is asserted by the ONE header that declares it, + so no two of these ever meet. */ +#ifndef SCHEMA_TABLE_STATIC_ASSERT +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) _Static_assert( cond, message ) +#elif defined( __cplusplus ) && __cplusplus >= 201103L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) static_assert( cond, message ) +#else +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) typedef char schema_table_assert_##tag[ ( cond ) ? 1 : -1 ] +#endif +#endif + +/* A record's ALIGNMENT, which the cook's layout contract asserts and which the + cook's Open compares the header's alignment word against (§7.1, §20.3). + C99 has no alignof either; C11's _Alignof, the two GNU spellings and MSVC's + cover every compiler this repo builds under, and the last form is the + portable definition of the same number. */ +#ifndef SCHEMA_TABLE_ALIGNOF +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_ALIGNOF( T ) _Alignof( T ) +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof__( T ) +#elif defined( _MSC_VER ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof( T ) +#else +#define SCHEMA_TABLE_ALIGNOF( T ) ( (size_t) offsetof( struct { char schema_pad_; T schema_member_; }, schema_member_ ) ) +#endif +#endif + +/* The table-wire read report — the permissive contract's ledger. Silence + (all zero) means the data matched this reader's schema exactly. */ +typedef struct TableReport +{ + int32_t unknown; /* unknown field ids skipped (newer data) */ + int32_t kind_mismatch; /* known id, changed type — skipped, never misdecoded */ + int32_t clamped; /* out-of-range values clamped to declared bounds */ + /* a key the TEXT form saw twice: last wins, and the repeat is counted + (docs/SPEC-TABLES.md §16.2). The wire never raises it — a body carrying an + id twice is legal input whose last occurrence wins, silently (§3). */ + int32_t duplicate; + int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ +} TableReport; + +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + +/* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- + + Static field descriptors for every type in the table closure: name, wire + id/kind, storage offset, bounds, ranges, enum names and branch guards — + enough to walk, print, diff, edit or bind any table value at runtime with + no schema files. _table_type() returns 's descriptor. */ + +struct TableTypeInfo; +struct TableFieldInfo; + +/* One arm of a union field: where its payload sits inside the union's storage + and what its payload looks like. The arm's NAME and its table-wire id ride + in the field's own vocabulary table at the same tag, so nothing is spelled + twice (docs/SPEC-TABLES.md §8). */ +typedef struct TableUnionArmInfo +{ + uint32_t offset; /* offsetof the arm's payload within the union storage */ + const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ +} TableUnionArmInfo; + +/* A union field's shape: the tag, and the arms indexed by it. Arms run + [0, enum_max]; index 0 is the EMPTY arm and carries no payload. */ +typedef struct TableUnionInfo +{ + uint32_t tag_offset; /* offsetof the tag within the union storage */ + uint32_t tag_size; /* sizeof the tag */ + const TableUnionArmInfo * arms; +} TableUnionInfo; + +/* ONE ENTRY OF A VOCABULARY, and the whole of what C++ spells as a pair of + captureless lambdas. An enum's values, a union's arms and a flags field's + BITS are each a named set indexed by [0, enum_max], so the set rides as an + ARRAY indexed the same way: no function per enum, and therefore no name per + enum for a schema to collide with (docs/SPEC-TABLES.md §11). + + name is the variant's spelling, or NULL for a value the declared set does + not name. id is the TABLE-WIRE id it rides under (§5) — the hash of the + variant's name, 0 for the reserved None and for the empty union arm. A + FLAGS field's variants are BIT POSITIONS and have no per-variant wire id + (§4), so has_ids is 0 there and 1 for an enum and a union: a vocabulary + with names and no ids is what says "flags". */ +typedef struct TableVariantInfo +{ + const char * name; + uint64_t id; +} TableVariantInfo; + +/* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// + block carries a doc column pointing at this one object, so absence costs a + unit no string data and a printer concatenates doc columns with no null + test. One definition per translation unit, and the descriptors of a unit + are one, so every absent doc compares equal by address. */ +static SCHEMA_UNUSED const char TableDocNone[1] = ""; + +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + +typedef struct TableFieldInfo +{ + const char * name; /* schema field name, e.g. "health" */ + const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ + const char * type_name; /* schema type name, e.g. "float32", "Grade" */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ + int is_array; /* fixed or counted array (bytes included) */ + int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ + int optional; /* a ?T field: a _present companion decides whether it rides */ + int32_t array_bound; /* array capacity / string max length; 0 for plain scalars */ + uint32_t offset; /* offsetof the storage member */ + uint32_t elem_size; /* sizeof the member (element size for arrays) */ + uint32_t count_offset; /* offsetof the _count/_length companion, or 0xffffffff */ + uint32_t present_offset; /* offsetof the _present companion, or 0xffffffff */ + const struct TableTypeInfo * table; /* nested table's descriptor, or NULL */ + int has_range; /* a declared [min, max] (int or float) */ + double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ + double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; + int64_t enum_max; /* enums: highest valid value (None = 0 always valid); + unions: the arm count (tag range [0, enum_max]); + flags: the highest declared BIT INDEX; else -1 */ + /* the vocabulary, indexed the same way enum_max bounds: an enum's value -> + (name, id), a union's tag -> (arm name, arm id), a FLAGS field's bit + index -> (variant name, 0). NULL for every other kind. */ + const TableVariantInfo * variants; + int has_variant_ids; /* 1 for an enum or a union, 0 for flags (§4) */ + /* an ENUM-KEYED array (docs/SPEC-TABLES.md §2.4): the array has one slot per + variant of key_type_name, the key k at index k-1, and its slots ride + under variant ids rather than positions. keys is the key's vocabulary, + indexed by the KEY — walk [0, array_bound) and ask about index + 1 to + print slots by name. NULL on every other field. */ + const char * key_type_name; + const TableVariantInfo * keys; + int64_t key_max; /* the key enum's highest value, bounding keys */ + /* union fields: the tag and its arms. NULL for every other kind. */ + const TableUnionInfo * arms; + const char * guard; /* branch guard, e.g. "at_rest" or "!at_rest"; "" if unguarded */ + /* what a PERSON wrote about the field (docs/SPEC-TABLES.md §8.1): the /// + block above it, verbatim (SPEC §4.1). It is TableDocNone when there is + none, never NULL. Its tags (SPEC §4.2) follow in declared order, and an + untagged field is 0 beside NULL. Static, constant-initialized, + allocating nothing. */ + const char * doc; + int32_t num_tags; + const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); +} TableFieldInfo; + +typedef struct TableTypeInfo +{ + const char * name; /* schema type name */ + uint32_t size; /* sizeof the storage struct */ + int32_t num_fields; + const TableFieldInfo * fields; + /* put one instance back at its declared defaults, in place. A generic + walker that fills a value has to be able to establish the defaults an + absent field takes, and it holds no type to spell — this is the one + thing the descriptors could not express without it. It is the void * + form of _reset and the same code. */ + void (*reset)( void * storage ); + /* the declaration's own doc and tags, on the same terms as a field's + (docs/SPEC-TABLES.md §8.1) */ + const char * doc; + int32_t num_tags; + const char * const * tags; +} TableTypeInfo; + + + +static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } +static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } +static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } +static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } + +#endif /* SCHEMA_LUDICROUS_TABLE_PRIMITIVES */ + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void degenerate_probe_reset( DegenerateProbe * value ); +static SCHEMA_UNUSED void schema_ludicrous_degenerate_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void fixed_probe_reset( FixedProbe * value ); +static SCHEMA_UNUSED void schema_ludicrous_fixed_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void fixed_quat_reset( FixedQuat * value ); +static SCHEMA_UNUSED void schema_ludicrous_fixed_quat_reset_raw_( void * storage ); +static SCHEMA_UNUSED void fixed_vec_reset( FixedVec * value ); +static SCHEMA_UNUSED void schema_ludicrous_fixed_vec_reset_raw_( void * storage ); +static SCHEMA_UNUSED void ludicrous_state_reset( LudicrousState * value ); +static SCHEMA_UNUSED void schema_ludicrous_ludicrous_state_reset_raw_( void * storage ); +static SCHEMA_UNUSED void unsigned_probe_reset( UnsignedProbe * value ); +static SCHEMA_UNUSED void schema_ludicrous_unsigned_probe_reset_raw_( void * storage ); +static SCHEMA_UNUSED void wide_probe_reset( WideProbe * value ); +static SCHEMA_UNUSED void schema_ludicrous_wide_probe_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void degenerate_probe_reset( DegenerateProbe * value ) +{ + value->locked_fixed = -196608; + value->locked_int = 7; + value->locked_wide = serialize_int128_make( 18446744073709551615ull, 18446731728030650382ull ); + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_ludicrous_degenerate_probe_reset_raw_( void * storage ) { degenerate_probe_reset( (DegenerateProbe *) storage ); } + +extern const TableTypeInfo schema_ludicrous_degenerate_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * degenerate_probe_table_type(void) { return &schema_ludicrous_degenerate_probe_info_; } +static SCHEMA_UNUSED void fixed_probe_reset( FixedProbe * value ) +{ + value->angle = 0; + value->position = 0; + value->reach = serialize_int128_make( 0ull, 0ull ); + value->ticks = 0; + memset( value->samples, 0, sizeof( value->samples ) ); +} + +static SCHEMA_UNUSED void schema_ludicrous_fixed_probe_reset_raw_( void * storage ) { fixed_probe_reset( (FixedProbe *) storage ); } + +extern const TableTypeInfo schema_ludicrous_fixed_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * fixed_probe_table_type(void) { return &schema_ludicrous_fixed_probe_info_; } +static SCHEMA_UNUSED void fixed_quat_reset( FixedQuat * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; + value->w = 1073741824; +} + +static SCHEMA_UNUSED void schema_ludicrous_fixed_quat_reset_raw_( void * storage ) { fixed_quat_reset( (FixedQuat *) storage ); } + +extern const TableTypeInfo schema_ludicrous_fixed_quat_info_; +static SCHEMA_UNUSED const TableTypeInfo * fixed_quat_table_type(void) { return &schema_ludicrous_fixed_quat_info_; } +static SCHEMA_UNUSED void fixed_vec_reset( FixedVec * value ) +{ + value->x = 0; + value->y = 0; + value->z = 0; +} + +static SCHEMA_UNUSED void schema_ludicrous_fixed_vec_reset_raw_( void * storage ) { fixed_vec_reset( (FixedVec *) storage ); } + +extern const TableTypeInfo schema_ludicrous_fixed_vec_info_; +static SCHEMA_UNUSED const TableTypeInfo * fixed_vec_table_type(void) { return &schema_ludicrous_fixed_vec_info_; } +static SCHEMA_UNUSED void ludicrous_state_reset( LudicrousState * value ) +{ + value->mode = DRIVE_MODE_NONE; + fixed_probe_reset( &value->probe ); + wide_probe_reset( &value->wide ); + memset( value->keys, 0, sizeof( value->keys ) ); + value->keys_count = 0; + value->has_target = 0; + value->target_id = serialize_uint128_make( 0ull, 0ull ); +} + +static SCHEMA_UNUSED void schema_ludicrous_ludicrous_state_reset_raw_( void * storage ) { ludicrous_state_reset( (LudicrousState *) storage ); } + +extern const TableTypeInfo schema_ludicrous_ludicrous_state_info_; +static SCHEMA_UNUSED const TableTypeInfo * ludicrous_state_table_type(void) { return &schema_ludicrous_ludicrous_state_info_; } +static SCHEMA_UNUSED void unsigned_probe_reset( UnsignedProbe * value ) +{ + value->angle = 0; + value->span = 0; + value->reach = serialize_uint128_make( 0ull, 0ull ); + value->ticks = 0; + memset( value->samples, 0, sizeof( value->samples ) ); + value->locked = 196608; + value->tail = 0; +} + +static SCHEMA_UNUSED void schema_ludicrous_unsigned_probe_reset_raw_( void * storage ) { unsigned_probe_reset( (UnsignedProbe *) storage ); } + +extern const TableTypeInfo schema_ludicrous_unsigned_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * unsigned_probe_table_type(void) { return &schema_ludicrous_unsigned_probe_info_; } +static SCHEMA_UNUSED void wide_probe_reset( WideProbe * value ) +{ + value->entity_id = serialize_uint128_make( 0ull, 0ull ); + value->energy = serialize_int128_make( 0ull, 0ull ); + value->flux = serialize_int128_make( 0ull, 0ull ); + value->bias = serialize_int128_make( 18446744073709551615ull, 18446744073709551366ull ); + value->seed = serialize_uint128_make( 2ull, 0ull ); +} + +static SCHEMA_UNUSED void schema_ludicrous_wide_probe_reset_raw_( void * storage ) { wide_probe_reset( (WideProbe *) storage ); } + +extern const TableTypeInfo schema_ludicrous_wide_probe_info_; +static SCHEMA_UNUSED const TableTypeInfo * wide_probe_table_type(void) { return &schema_ludicrous_wide_probe_info_; } +const UnitViewInfo * schema_ludicrous_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_ludicrous_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/testdata/golden/packet-wide/c/WideView.c b/testdata/golden/packet-wide/c/WideView.c new file mode 100644 index 000000000..85c404df3 --- /dev/null +++ b/testdata/golden/packet-wide/c/WideView.c @@ -0,0 +1,44 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "WideView.h" +static const TableFieldInfo schema_wide_narrow_fifteen_fields_[] = { + { "text", NULL, "string", 0x0000000000000000ull, 12, 0, 1, 0, 15, (uint32_t) offsetof( NarrowFifteen, text ), (uint32_t) sizeof( ( (NarrowFifteen *) 0 )->text ), (uint32_t) offsetof( NarrowFifteen, text_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_wide_narrow_fifteen_info_ = { "NarrowFifteen", (uint32_t) sizeof( NarrowFifteen ), 1, schema_wide_narrow_fifteen_fields_, schema_wide_narrow_fifteen_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_wide_wide_four_fields_[] = { + { "text", NULL, "wstring", 0x0000000000000000ull, 33, 0, 1, 0, 4, (uint32_t) offsetof( WideFour, text ), (uint32_t) sizeof( ( (WideFour *) 0 )->text ), (uint32_t) offsetof( WideFour, text_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_wide_wide_four_info_ = { "WideFour", (uint32_t) sizeof( WideFour ), 1, schema_wide_wide_four_fields_, schema_wide_wide_four_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_wide_wide_interop_fields_[] = { + { "caption", NULL, "wstring", 0x0000000000000000ull, 33, 0, 1, 0, 7, (uint32_t) offsetof( WideInterop, caption ), (uint32_t) sizeof( ( (WideInterop *) 0 )->caption ), (uint32_t) offsetof( WideInterop, caption_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_wide_wide_interop_info_ = { "WideInterop", (uint32_t) sizeof( WideInterop ), 1, schema_wide_wide_interop_fields_, schema_wide_wide_interop_reset_raw_, TableDocNone, 0, NULL }; + +static const TableFieldInfo schema_wide_wide_seven_fields_[] = { + { "text", NULL, "wstring", 0x0000000000000000ull, 33, 0, 1, 0, 7, (uint32_t) offsetof( WideSeven, text ), (uint32_t) sizeof( ( (WideSeven *) 0 )->text ), (uint32_t) offsetof( WideSeven, text_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, +}; + +const TableTypeInfo schema_wide_wide_seven_info_ = { "WideSeven", (uint32_t) sizeof( WideSeven ), 1, schema_wide_wide_seven_fields_, schema_wide_wide_seven_reset_raw_, TableDocNone, 0, NULL }; + +const UnitViewInfo * schema_wide_unit_view_(void) +{ + static const ViewType types[] = { + { "NarrowFifteen", "WideText.schema", 0, &schema_wide_narrow_fifteen_info_, TableDocNone, 0, NULL }, + { "WideFour", "WideText.schema", 0, &schema_wide_wide_four_info_, TableDocNone, 0, NULL }, + { "WideInterop", "WideText.schema", 0, &schema_wide_wide_interop_info_, TableDocNone, 0, NULL }, + { "WideSeven", "WideText.schema", 0, &schema_wide_wide_seven_info_, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "wide", 0x53835dbf9f05e4d9ull, + 4, types, + 0, NULL, + 0, NULL, + 0, NULL, + 0, NULL, + 0, NULL }; + return &info; +} diff --git a/testdata/golden/packet-wide/c/WideView.h b/testdata/golden/packet-wide/c/WideView.h new file mode 100644 index 000000000..1b7f8dc6e --- /dev/null +++ b/testdata/golden/packet-wide/c/WideView.h @@ -0,0 +1,374 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_WIDE_VIEW_H +#define SCHEMA_WIDE_VIEW_H +#include +#include +#include +#include "WideText.h" +#ifndef SCHEMA_WIDE_TABLE_PRIMITIVES +#define SCHEMA_WIDE_TABLE_PRIMITIVES + +/* THE CODEC DOES NOT DEPEND ON THE COMPILER'S INLINING BUDGET. A table of a + realistic field count emits one large body per type, and the cursor a body + writes through lives in the caller's TableWriter: across a call boundary that + cursor round-trips through memory, and a uint8_t * store may alias the writer + itself, so every put reloads it. When a budget runs out mid-body the codec + silently degrades to that shape. Forcing the primitives and the fixed-class + bodies inline is what keeps the cursor in registers and lets adjacent + constant framing bytes merge into one store. + + The spelling is FEATURE TESTED rather than assumed, exactly as the packet + emitter's own inlining demand is (SPEC §6.1's C column). + + IT CARRIES THE PACKAGE for the reason every other name here does, and that + reason is the LINKER rather than the preprocessor: two units of the same + schema family link into one program — two generations of one schema is the + case the conformance driver itself is — so nothing this backend emits may + be spelled the same way by two packages. They do NOT share a translation + unit and cannot: C has no namespace, so two units' headers in one TU + redefine TableReport, TableWriter and every other runtime name, which is the + packet emitter's standing limit (SPEC §6.1) and not something tables + changed. A per-package macro is what keeps the LINK legal. */ +#ifndef SCHEMA_WIDE_TABLE_INLINE +#if defined( _MSC_VER ) +#define SCHEMA_WIDE_TABLE_INLINE __forceinline +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_WIDE_TABLE_INLINE inline __attribute__(( always_inline )) +#else +#define SCHEMA_WIDE_TABLE_INLINE inline +#endif +#endif + +/* A COMPILE-TIME assertion under -std=c99, which has none of its own. C11's + _Static_assert carries the message into the build log verbatim and is used + wherever the compiler has it; everywhere else the negative array bound is + the same refusal with a tag a reader can find. The tag is unique per record + and field, and each record is asserted by the ONE header that declares it, + so no two of these ever meet. */ +#ifndef SCHEMA_TABLE_STATIC_ASSERT +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) _Static_assert( cond, message ) +#elif defined( __cplusplus ) && __cplusplus >= 201103L +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) static_assert( cond, message ) +#else +#define SCHEMA_TABLE_STATIC_ASSERT( tag, cond, message ) typedef char schema_table_assert_##tag[ ( cond ) ? 1 : -1 ] +#endif +#endif + +/* A record's ALIGNMENT, which the cook's layout contract asserts and which the + cook's Open compares the header's alignment word against (§7.1, §20.3). + C99 has no alignof either; C11's _Alignof, the two GNU spellings and MSVC's + cover every compiler this repo builds under, and the last form is the + portable definition of the same number. */ +#ifndef SCHEMA_TABLE_ALIGNOF +#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L +#define SCHEMA_TABLE_ALIGNOF( T ) _Alignof( T ) +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof__( T ) +#elif defined( _MSC_VER ) +#define SCHEMA_TABLE_ALIGNOF( T ) __alignof( T ) +#else +#define SCHEMA_TABLE_ALIGNOF( T ) ( (size_t) offsetof( struct { char schema_pad_; T schema_member_; }, schema_member_ ) ) +#endif +#endif + +/* The table-wire read report — the permissive contract's ledger. Silence + (all zero) means the data matched this reader's schema exactly. */ +typedef struct TableReport +{ + int32_t unknown; /* unknown field ids skipped (newer data) */ + int32_t kind_mismatch; /* known id, changed type — skipped, never misdecoded */ + int32_t clamped; /* out-of-range values clamped to declared bounds */ + /* a key the TEXT form saw twice: last wins, and the repeat is counted + (docs/SPEC-TABLES.md §16.2). The wire never raises it — a body carrying an + id twice is legal input whose last occurrence wins, silently (§3). */ + int32_t duplicate; + int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ +} TableReport; + +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + +/* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- + + Static field descriptors for every type in the table closure: name, wire + id/kind, storage offset, bounds, ranges, enum names and branch guards — + enough to walk, print, diff, edit or bind any table value at runtime with + no schema files. _table_type() returns 's descriptor. */ + +struct TableTypeInfo; +struct TableFieldInfo; + +/* One arm of a union field: where its payload sits inside the union's storage + and what its payload looks like. The arm's NAME and its table-wire id ride + in the field's own vocabulary table at the same tag, so nothing is spelled + twice (docs/SPEC-TABLES.md §8). */ +typedef struct TableUnionArmInfo +{ + uint32_t offset; /* offsetof the arm's payload within the union storage */ + const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ +} TableUnionArmInfo; + +/* A union field's shape: the tag, and the arms indexed by it. Arms run + [0, enum_max]; index 0 is the EMPTY arm and carries no payload. */ +typedef struct TableUnionInfo +{ + uint32_t tag_offset; /* offsetof the tag within the union storage */ + uint32_t tag_size; /* sizeof the tag */ + const TableUnionArmInfo * arms; +} TableUnionInfo; + +/* ONE ENTRY OF A VOCABULARY, and the whole of what C++ spells as a pair of + captureless lambdas. An enum's values, a union's arms and a flags field's + BITS are each a named set indexed by [0, enum_max], so the set rides as an + ARRAY indexed the same way: no function per enum, and therefore no name per + enum for a schema to collide with (docs/SPEC-TABLES.md §11). + + name is the variant's spelling, or NULL for a value the declared set does + not name. id is the TABLE-WIRE id it rides under (§5) — the hash of the + variant's name, 0 for the reserved None and for the empty union arm. A + FLAGS field's variants are BIT POSITIONS and have no per-variant wire id + (§4), so has_ids is 0 there and 1 for an enum and a union: a vocabulary + with names and no ids is what says "flags". */ +typedef struct TableVariantInfo +{ + const char * name; + uint64_t id; +} TableVariantInfo; + +/* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// + block carries a doc column pointing at this one object, so absence costs a + unit no string data and a printer concatenates doc columns with no null + test. One definition per translation unit, and the descriptors of a unit + are one, so every absent doc compares equal by address. */ +static SCHEMA_UNUSED const char TableDocNone[1] = ""; + +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + +typedef struct TableFieldInfo +{ + const char * name; /* schema field name, e.g. "health" */ + const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ + const char * type_name; /* schema type name, e.g. "float32", "Grade" */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ + int is_array; /* fixed or counted array (bytes included) */ + int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ + int optional; /* a ?T field: a _present companion decides whether it rides */ + int32_t array_bound; /* array capacity / string max length; 0 for plain scalars */ + uint32_t offset; /* offsetof the storage member */ + uint32_t elem_size; /* sizeof the member (element size for arrays) */ + uint32_t count_offset; /* offsetof the _count/_length companion, or 0xffffffff */ + uint32_t present_offset; /* offsetof the _present companion, or 0xffffffff */ + const struct TableTypeInfo * table; /* nested table's descriptor, or NULL */ + int has_range; /* a declared [min, max] (int or float) */ + double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ + double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; + int64_t enum_max; /* enums: highest valid value (None = 0 always valid); + unions: the arm count (tag range [0, enum_max]); + flags: the highest declared BIT INDEX; else -1 */ + /* the vocabulary, indexed the same way enum_max bounds: an enum's value -> + (name, id), a union's tag -> (arm name, arm id), a FLAGS field's bit + index -> (variant name, 0). NULL for every other kind. */ + const TableVariantInfo * variants; + int has_variant_ids; /* 1 for an enum or a union, 0 for flags (§4) */ + /* an ENUM-KEYED array (docs/SPEC-TABLES.md §2.4): the array has one slot per + variant of key_type_name, the key k at index k-1, and its slots ride + under variant ids rather than positions. keys is the key's vocabulary, + indexed by the KEY — walk [0, array_bound) and ask about index + 1 to + print slots by name. NULL on every other field. */ + const char * key_type_name; + const TableVariantInfo * keys; + int64_t key_max; /* the key enum's highest value, bounding keys */ + /* union fields: the tag and its arms. NULL for every other kind. */ + const TableUnionInfo * arms; + const char * guard; /* branch guard, e.g. "at_rest" or "!at_rest"; "" if unguarded */ + /* what a PERSON wrote about the field (docs/SPEC-TABLES.md §8.1): the /// + block above it, verbatim (SPEC §4.1). It is TableDocNone when there is + none, never NULL. Its tags (SPEC §4.2) follow in declared order, and an + untagged field is 0 beside NULL. Static, constant-initialized, + allocating nothing. */ + const char * doc; + int32_t num_tags; + const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); +} TableFieldInfo; + +typedef struct TableTypeInfo +{ + const char * name; /* schema type name */ + uint32_t size; /* sizeof the storage struct */ + int32_t num_fields; + const TableFieldInfo * fields; + /* put one instance back at its declared defaults, in place. A generic + walker that fills a value has to be able to establish the defaults an + absent field takes, and it holds no type to spell — this is the one + thing the descriptors could not express without it. It is the void * + form of _reset and the same code. */ + void (*reset)( void * storage ); + /* the declaration's own doc and tags, on the same terms as a field's + (docs/SPEC-TABLES.md §8.1) */ + const char * doc; + int32_t num_tags; + const char * const * tags; +} TableTypeInfo; + + + +static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } +static SCHEMA_UNUSED uint32_t table_float_to_bits( float f ) { uint32_t b; memcpy( &b, &f, 4 ); return b; } +static SCHEMA_UNUSED double table_bits_to_double( uint64_t bits ) { double d; memcpy( &d, &bits, 8 ); return d; } +static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; memcpy( &b, &d, 8 ); return b; } + +#endif /* SCHEMA_WIDE_TABLE_PRIMITIVES */ + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void narrow_fifteen_reset( NarrowFifteen * value ); +static SCHEMA_UNUSED void schema_wide_narrow_fifteen_reset_raw_( void * storage ); +static SCHEMA_UNUSED void wide_four_reset( WideFour * value ); +static SCHEMA_UNUSED void schema_wide_wide_four_reset_raw_( void * storage ); +static SCHEMA_UNUSED void wide_interop_reset( WideInterop * value ); +static SCHEMA_UNUSED void schema_wide_wide_interop_reset_raw_( void * storage ); +static SCHEMA_UNUSED void wide_seven_reset( WideSeven * value ); +static SCHEMA_UNUSED void schema_wide_wide_seven_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void narrow_fifteen_reset( NarrowFifteen * value ) +{ + memset( value->text, 0, sizeof( value->text ) ); + value->text_length = 0; +} + +static SCHEMA_UNUSED void schema_wide_narrow_fifteen_reset_raw_( void * storage ) { narrow_fifteen_reset( (NarrowFifteen *) storage ); } + +extern const TableTypeInfo schema_wide_narrow_fifteen_info_; +static SCHEMA_UNUSED const TableTypeInfo * narrow_fifteen_table_type(void) { return &schema_wide_narrow_fifteen_info_; } +static SCHEMA_UNUSED void wide_four_reset( WideFour * value ) +{ + memset( value->text, 0, sizeof( value->text ) ); + value->text_length = 0; +} + +static SCHEMA_UNUSED void schema_wide_wide_four_reset_raw_( void * storage ) { wide_four_reset( (WideFour *) storage ); } + +extern const TableTypeInfo schema_wide_wide_four_info_; +static SCHEMA_UNUSED const TableTypeInfo * wide_four_table_type(void) { return &schema_wide_wide_four_info_; } +static SCHEMA_UNUSED void wide_interop_reset( WideInterop * value ) +{ + memset( value->caption, 0, sizeof( value->caption ) ); + value->caption_length = 0; +} + +static SCHEMA_UNUSED void schema_wide_wide_interop_reset_raw_( void * storage ) { wide_interop_reset( (WideInterop *) storage ); } + +extern const TableTypeInfo schema_wide_wide_interop_info_; +static SCHEMA_UNUSED const TableTypeInfo * wide_interop_table_type(void) { return &schema_wide_wide_interop_info_; } +static SCHEMA_UNUSED void wide_seven_reset( WideSeven * value ) +{ + memset( value->text, 0, sizeof( value->text ) ); + value->text_length = 0; +} + +static SCHEMA_UNUSED void schema_wide_wide_seven_reset_raw_( void * storage ) { wide_seven_reset( (WideSeven *) storage ); } + +extern const TableTypeInfo schema_wide_wide_seven_info_; +static SCHEMA_UNUSED const TableTypeInfo * wide_seven_table_type(void) { return &schema_wide_wide_seven_info_; } +const UnitViewInfo * schema_wide_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_wide_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/testdata/golden/tables/block-c/BlockdemoView.c b/testdata/golden/tables/block-c/BlockdemoView.c new file mode 100644 index 000000000..eaf97391c --- /dev/null +++ b/testdata/golden/tables/block-c/BlockdemoView.c @@ -0,0 +1,96 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "BlockdemoView.h" +const UnitViewInfo * schema_blockdemo_unit_view_(void) +{ + static const ViewVariant ExplosionType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Small", 0x3d2cc8d952adebecull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Large", 0xc8736ef79380e634ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant LaserType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Pulse", 0x94e8a172d8f4805eull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Beam", 0xa0745aa7967eeffaull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant MissileType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Seeker", 0xf5d0a4d25a76afcaull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Dumb", 0x478d7972f6d9075dull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant PropType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Rock", 0xcaa6172bef74e234ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Station", 0x4f98acac2852d653ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Beacon", 0x7f3a7e4744ea3019ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ShipType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Fighter", 0xd011f7c3c15285c2ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Bomber", 0xa8216aa1c554cb8aull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Freighter", 0x6c2321a3d00e23dbull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Team_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Red", 0x9ff1de19feac1b7cull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Blue", 0xecf3d3a7c1693e2dull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Green", 0xcf00d78fd5953f1cull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 4, "Gold", 0xc416c97e0d3218b3ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ShipFlags_view_variants[] = { + { 0, "FiringLaser", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Boosting", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Braking", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Aiming", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "ExplosionType", "Render.schema", 2, 8, 3, ExplosionType_view_variants, TableDocNone, 0, NULL }, + { "LaserType", "Render.schema", 2, 8, 3, LaserType_view_variants, TableDocNone, 0, NULL }, + { "MissileType", "Render.schema", 2, 8, 3, MissileType_view_variants, TableDocNone, 0, NULL }, + { "PropType", "Render.schema", 3, 8, 4, PropType_view_variants, TableDocNone, 0, NULL }, + { "ShipType", "Render.schema", 3, 8, 4, ShipType_view_variants, TableDocNone, 0, NULL }, + { "Team", "Render.schema", 4, 8, 5, Team_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary flags_decls[] = { + { "ShipFlags", "Render.schema", 3, 8, 4, ShipFlags_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "RenderQuaternion", "Render.schema", 0, &schema_blockdemo_render_quaternion_info_, TableDocNone, 0, NULL }, + { "RenderVector3", "Render.schema", 0, &schema_blockdemo_render_vector3_info_, TableDocNone, 0, NULL }, + }; + static const ViewType tables[] = { + { "PaddedFrame", "Padded.schema", 1, &schema_blockdemo_padded_frame_info_, TableDocNone, 0, NULL }, + { "PaddedRow", "Padded.schema", 1, &schema_blockdemo_padded_row_info_, TableDocNone, 0, NULL }, + { "RenderCamera", "Render.schema", 1, &schema_blockdemo_render_camera_info_, TableDocNone, 0, NULL }, + { "RenderCosmeticProp", "Render.schema", 1, &schema_blockdemo_render_cosmetic_prop_info_, TableDocNone, 0, NULL }, + { "RenderDynamicProp", "Render.schema", 1, &schema_blockdemo_render_dynamic_prop_info_, TableDocNone, 0, NULL }, + { "RenderExplosion", "Render.schema", 1, &schema_blockdemo_render_explosion_info_, TableDocNone, 0, NULL }, + { "RenderFrame", "Render.schema", 1, &schema_blockdemo_render_frame_info_, TableDocNone, 0, NULL }, + { "RenderLaser", "Render.schema", 1, &schema_blockdemo_render_laser_info_, TableDocNone, 0, NULL }, + { "RenderMissile", "Render.schema", 1, &schema_blockdemo_render_missile_info_, TableDocNone, 0, NULL }, + { "RenderShip", "Render.schema", 1, &schema_blockdemo_render_ship_info_, TableDocNone, 0, NULL }, + { "RenderStaticProp", "Render.schema", 1, &schema_blockdemo_render_static_prop_info_, TableDocNone, 0, NULL }, + { "RenderTurret", "Render.schema", 1, &schema_blockdemo_render_turret_info_, TableDocNone, 0, NULL }, + }; + static const ViewConstant constants[] = { + { "MaxCameras", "Render.schema", "int64", 0, INT64_C(1), 0.0, TableDocNone, 0, NULL }, + { "MaxCosmeticProps", "Render.schema", "int64", 0, INT64_C(8192), 0.0, TableDocNone, 0, NULL }, + { "MaxDynamicProps", "Render.schema", "int64", 0, INT64_C(4096), 0.0, TableDocNone, 0, NULL }, + { "MaxExplosions", "Render.schema", "int64", 0, INT64_C(32000), 0.0, TableDocNone, 0, NULL }, + { "MaxLasers", "Render.schema", "int64", 0, INT64_C(32000), 0.0, TableDocNone, 0, NULL }, + { "MaxMissiles", "Render.schema", "int64", 0, INT64_C(4096), 0.0, TableDocNone, 0, NULL }, + { "MaxPaddedRows", "Padded.schema", "int64", 0, INT64_C(64), 0.0, TableDocNone, 0, NULL }, + { "MaxShips", "Render.schema", "int64", 0, INT64_C(4096), 0.0, TableDocNone, 0, NULL }, + { "MaxStaticProps", "Render.schema", "int64", 0, INT64_C(20000), 0.0, TableDocNone, 0, NULL }, + { "MaxTurrets", "Render.schema", "int64", 0, INT64_C(1024), 0.0, TableDocNone, 0, NULL }, + { "RenderVersion", "Render.schema", "int64", 0, INT64_C(1), 0.0, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "blockdemo", 0x802d031e1b4cdb0cull, + 2, types, + 12, tables, + 6, enums, + 1, flags_decls, + 0, NULL, + 11, constants }; + return &info; +} diff --git a/testdata/golden/tables/block-c/BlockdemoView.h b/testdata/golden/tables/block-c/BlockdemoView.h new file mode 100644 index 000000000..d10097e88 --- /dev/null +++ b/testdata/golden/tables/block-c/BlockdemoView.h @@ -0,0 +1,105 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_BLOCKDEMO_VIEW_H +#define SCHEMA_BLOCKDEMO_VIEW_H +#include +#include +#include +#include "Padded.h" +#include "Render.h" +#include "PaddedTable.h" +#include "RenderTable.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + + +const UnitViewInfo * schema_blockdemo_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_blockdemo_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/testdata/golden/tables/block-c/PaddedTable.c b/testdata/golden/tables/block-c/PaddedTable.c index f789dd0a1..cd714dd45 100644 --- a/testdata/golden/tables/block-c/PaddedTable.c +++ b/testdata/golden/tables/block-c/PaddedTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) - { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,30 +2330,30 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableVariantInfo schema_blockdemo_padded_row_teams_keys_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_padded_row_fields_[] = { - { "tag", "tag", "uint8", 0xbc64, 6, 0, 0, 0, 0, (uint32_t) offsetof( PaddedRow, tag ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->tag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "value", "value", "float64", 0x9194, 11, 0, 0, 0, 0, (uint32_t) offsetof( PaddedRow, value ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "flag", "flag", "bool", 0x153d, 1, 0, 0, 0, 0, (uint32_t) offsetof( PaddedRow, flag ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "id", "id", "uint32", 0x5dd8, 8, 0, 0, 0, 0, (uint32_t) offsetof( PaddedRow, id ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "label", "label", "string", 0xe16a, 12, 0, 1, 0, 15, (uint32_t) offsetof( PaddedRow, label ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->label ), (uint32_t) offsetof( PaddedRow, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "slots", "slots", "uint16", 0xf4d8, 7, 1, 0, 0, 4, (uint32_t) offsetof( PaddedRow, slots ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->slots[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "teams", "teams", "uint8", 0x9ae1, 6, 1, 0, 0, (int32_t) TEAM_MAX, (uint32_t) offsetof( PaddedRow, teams ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->teams[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, "Team", schema_blockdemo_padded_row_teams_keys_, 4, NULL, "", TableDocNone, 0, NULL }, - { "counter", "counter", "int32", 0x428f, 4, 0, 0, 1, 0, (uint32_t) offsetof( PaddedRow, counter ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->counter ), 0xffffffffu, (uint32_t) offsetof( PaddedRow, counter_present ), NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "tag", "tag", "uint8", 0x56d7ab194448a4f3ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( PaddedRow, tag ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->tag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "value", "value", "float64", 0x7ce4fd9430e80ceaull, 11, 0, 0, 0, 0, (uint32_t) offsetof( PaddedRow, value ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flag", "flag", "bool", 0xd5f2d079088c0b17ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( PaddedRow, flag ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->flag ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "id", "id", "uint32", 0x08b72e07b55c3ac0ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( PaddedRow, id ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "label", "label", "string", 0x39f7fcec8fcb623dull, 12, 0, 1, 0, 15, (uint32_t) offsetof( PaddedRow, label ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->label ), (uint32_t) offsetof( PaddedRow, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "slots", "slots", "uint16", 0xe68c2e6bb1ee5646ull, 7, 1, 0, 0, 4, (uint32_t) offsetof( PaddedRow, slots ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->slots[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "teams", "teams", "uint8", 0xbaaeb048a5a8fa6dull, 6, 1, 0, 0, (int32_t) TEAM_MAX, (uint32_t) offsetof( PaddedRow, teams ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->teams[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, "Team", schema_blockdemo_padded_row_teams_keys_, 4, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "counter", "counter", "int32", 0x77976c7416517c63ull, 4, 0, 0, 1, 0, (uint32_t) offsetof( PaddedRow, counter ), (uint32_t) sizeof( ( (PaddedRow *) 0 )->counter ), 0xffffffffu, (uint32_t) offsetof( PaddedRow, counter_present ), NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_padded_row_info_ = { "PaddedRow", (uint32_t) sizeof( PaddedRow ), 8, schema_blockdemo_padded_row_fields_, schema_blockdemo_padded_row_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_blockdemo_padded_frame_fields_[] = { - { "marker", "marker", "uint8", 0x866f, 6, 0, 0, 0, 0, (uint32_t) offsetof( PaddedFrame, marker ), (uint32_t) sizeof( ( (PaddedFrame *) 0 )->marker ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "stamp", "stamp", "uint64", 0x0dc6, 9, 0, 0, 0, 0, (uint32_t) offsetof( PaddedFrame, stamp ), (uint32_t) sizeof( ( (PaddedFrame *) 0 )->stamp ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "rows", "rows", "PaddedRow", 0x99af, 13, 1, 1, 0, 64, (uint32_t) offsetof( PaddedFrame, rows ), (uint32_t) sizeof( ( (PaddedFrame *) 0 )->rows[0] ), (uint32_t) offsetof( PaddedFrame, rows_count ), 0xffffffffu, &schema_blockdemo_padded_row_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "blob", "blob", "bytes", 0xd316, 6, 1, 1, 0, 12, (uint32_t) offsetof( PaddedFrame, blob ), (uint32_t) sizeof( ( (PaddedFrame *) 0 )->blob[0] ), (uint32_t) offsetof( PaddedFrame, blob_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "marker", "marker", "uint8", 0xeddcb72b15486e77ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( PaddedFrame, marker ), (uint32_t) sizeof( ( (PaddedFrame *) 0 )->marker ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "stamp", "stamp", "uint64", 0xee7ba9ad45c64144ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( PaddedFrame, stamp ), (uint32_t) sizeof( ( (PaddedFrame *) 0 )->stamp ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rows", "rows", "PaddedRow", 0xa3a7061ff10a8138ull, 13, 1, 1, 0, 64, (uint32_t) offsetof( PaddedFrame, rows ), (uint32_t) sizeof( ( (PaddedFrame *) 0 )->rows[0] ), (uint32_t) offsetof( PaddedFrame, rows_count ), 0xffffffffu, &schema_blockdemo_padded_row_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "blob", "blob", "bytes", 0xc573b39bc29148caull, 6, 1, 1, 0, 12, (uint32_t) offsetof( PaddedFrame, blob ), (uint32_t) sizeof( ( (PaddedFrame *) 0 )->blob[0] ), (uint32_t) offsetof( PaddedFrame, blob_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_padded_frame_info_ = { "PaddedFrame", (uint32_t) sizeof( PaddedFrame ), 4, schema_blockdemo_padded_frame_fields_, schema_blockdemo_padded_frame_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/block-c/PaddedTable.h b/testdata/golden/tables/block-c/PaddedTable.h index 438ed075e..98301cdd8 100644 --- a/testdata/golden/tables/block-c/PaddedTable.h +++ b/testdata/golden/tables/block-c/PaddedTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package blockdemo — protocol id 0x802d031e1b4cdb0c (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_BLOCKDEMO_PADDEDTABLE_H #define SCHEMA_BLOCKDEMO_PADDEDTABLE_H @@ -110,8 +110,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -120,6 +126,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -129,6 +136,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -155,7 +164,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -165,12 +174,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -184,6 +195,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -211,6 +224,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -232,157 +247,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[89]; + uint32_t slots[256], positions[89]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (256-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(256-1); + } + if(v->count==89) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } +} +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) +{ + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } } - return 0; + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -392,6 +527,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_BLOCKDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_BLOCKDEMO_BUILD_VERSION #define SCHEMA_BLOCKDEMO_BUILD_VERSION @@ -503,25 +693,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -531,40 +721,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_BLOCKDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table PaddedRow — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in padded_row_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -635,186 +1406,218 @@ static SCHEMA_UNUSED int64_t padded_row_measure( const PaddedRow * value ); static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_save_body( TableWriter * w, const PaddedRow * value ); static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_load_body( TableReader * r, PaddedRow * value ); static SCHEMA_UNUSED int64_t padded_frame_measure( const PaddedFrame * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_frame_save_body( TableWriter * w, const PaddedFrame * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_frame_load_body( TableReader * r, PaddedFrame * value ); +static SCHEMA_UNUSED int padded_frame_save_body( TableWriter * w, const PaddedFrame * value ); +static SCHEMA_UNUSED int padded_frame_load_body( TableReader * r, PaddedFrame * value ); + +static SCHEMA_UNUSED int schema_blockdemo_padded_row_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_padded_frame_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int padded_row_save_message_body(TableBitWriter *,const PaddedRow *); +static SCHEMA_UNUSED int padded_frame_save_message_body(TableBitWriter *,const PaddedFrame *); +static SCHEMA_UNUSED int padded_row_load_message_body(TableMessageReader *,PaddedRow *); +static SCHEMA_UNUSED int padded_frame_load_message_body(TableMessageReader *,PaddedFrame *); +static SCHEMA_UNUSED int schema_blockdemo_padded_row_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_padded_frame_message_extent_(TableMessageReader *,int64_t *); +#define SCHEMA_BLOCKDEMO_PADDED_ROW_TEAMS_AT(value,key) SCHEMA_TABLE_KEYED_AT((value).teams,(key),TEAM_MAX) +static SCHEMA_UNUSED int schema_blockdemo_padded_row_wire_label_( TableWriter * w, const PaddedRow * value ) +{ + if ( value->label_length < 0 || value->label_length > 15 ) { return 0; } + table_writer_raw( w, value->label, value->label_length ); + return !w->overflow; +} -static SCHEMA_UNUSED int64_t padded_row_measure( const PaddedRow * value ) +static SCHEMA_UNUSED int schema_blockdemo_padded_row_wire_slots_( TableWriter * w, const PaddedRow * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->tag != 0 ) { bytes += 3 + 1; } /* tag */ - if ( value->value != 0.0 ) { bytes += 3 + 8; } /* value */ - if ( value->flag != 0 ) { bytes += 3 + 1; } /* flag */ - if ( value->id != 0 ) { bytes += 3 + 4; } /* id */ - if ( value->label_length < 0 || value->label_length > 15 ) { return -1; } /* storage invariant */ - if ( value->label_length > 0 ) { bytes += 3 + 4 + value->label_length; } /* label */ + int32_t i; + table_writer_put8( w, 7 ); table_writer_leb( w, (uint64_t) 4 ); + for ( i = 0; i < 4; i++ ) { - int all_default_slots = 1; - int32_t i; - for ( i = 0; i < 4; i++ ) { if ( value->slots[i] != 0 ) { all_default_slots = 0; break; } } - if ( !all_default_slots ) - { - bytes += 3 + 4 + 5 + 8; /* slots */ - } + table_writer_put16( w, (uint16_t) ( value->slots[i] ) ); } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_padded_row_wire_teams_( TableWriter * w, const PaddedRow * value ) +{ + uint64_t count = 0; int32_t i; + for ( i = 0; i < TEAM_MAX; i++ ) { - int64_t pairs_teams = 0, body_teams = 0; - int32_t i; - for ( i = 0; i < TEAM_MAX; i++ ) /* [Team]: every stored slot is a named variant's */ - { - if ( value->teams[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - case TEAM_GOLD: key_id = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_teams++; body_teams += 2 + 4 + 1; /* key, length, element */ - } - if ( pairs_teams > 0 ) { bytes += 3 + 4 + 5 + body_teams; } /* teams */ + if ( value->teams[i] == 0 ) { continue; } + count++; } - if ( value->counter_present ) /* ?int32: presence decides, not content */ + table_writer_put8( w, 6 ); table_writer_leb( w, count ); + for ( i = 0; i < TEAM_MAX; i++ ) { - bytes += 3 + 4; /* counter */ + if ( value->teams[i] == 0 ) { continue; } + switch ( i + 1 ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } + { + TableWriter probe = table_writer_probe( w ); + TableWriter * outer = w; + w = &probe; + table_writer_put8( w, (uint8_t) ( value->teams[i] ) ); + w = outer; table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + table_writer_put8( w, (uint8_t) ( value->teams[i] ) ); + } } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_save_body( TableWriter * w, const PaddedRow * value ) +static SCHEMA_UNUSED int padded_row_save_body( TableWriter * w, const PaddedRow * value ) { - if ( value->tag != 0 ) - { - table_writer_put16( w, 0xbc64 ); table_writer_put8( w, 6 ); /* tag */ - table_writer_put8( w, (uint8_t) ( value->tag ) ); - } - if ( value->value != 0.0 ) - { - table_writer_put16( w, 0x9194 ); table_writer_put8( w, 11 ); /* value */ - table_writer_put64( w, table_double_to_bits( value->value ) ); + (void) value; + { /* tag */ + if ( !( value->tag == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x56d7ab194448a4f3ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->tag ) ); + } } - if ( value->flag != 0 ) - { - table_writer_put16( w, 0x153d ); table_writer_put8( w, 1 ); /* flag */ - table_writer_put8( w, value->flag ? 1 : 0 ); + { /* value */ + if ( !( value->value == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7ce4fd9430e80ceaull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->value ) ); + } } - if ( value->id != 0 ) - { - table_writer_put16( w, 0x5dd8 ); table_writer_put8( w, 8 ); /* id */ - table_writer_put32( w, (uint32_t) ( value->id ) ); + { /* flag */ + if ( !( value->flag == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd5f2d079088c0b17ull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->flag ? 1 : 0 ); + } } - if ( value->label_length < 0 || value->label_length > 15 ) { return 0; } /* storage invariant */ - if ( value->label_length > 0 ) - { - table_writer_put16( w, 0xe16a ); table_writer_put8( w, 12 ); /* label */ - table_writer_put32( w, (uint32_t) value->label_length ); - table_writer_raw( w, value->label, value->label_length ); + { /* id */ + if ( !( value->id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x08b72e07b55c3ac0ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->id ) ); + } } - { - int all_default_slots = 1; - int32_t i; - for ( i = 0; i < 4; i++ ) { if ( value->slots[i] != 0 ) { all_default_slots = 0; break; } } - if ( !all_default_slots ) + { /* label */ + if ( value->label_length < 0 || value->label_length > 15 ) { return 0; } + if ( value->label_length != 0 ) { - int64_t len_at_slots; - table_writer_put16( w, 0xf4d8 ); table_writer_put8( w, 14 ); /* slots (fixed [4]) */ - len_at_slots = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 7 ); table_writer_put32( w, 4 ); - for ( i = 0; i < 4; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x39f7fcec8fcb623dull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) { - table_writer_put16( w, (uint16_t) ( value->slots[i] ) ); + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_padded_row_wire_label_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_padded_row_wire_label_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_padded_row_wire_label_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_slots, (uint32_t) ( w->offset - len_at_slots - 4 ) ); } } - { - uint32_t pairs_teams = 0; - int32_t i; - for ( i = 0; i < TEAM_MAX; i++ ) /* [Team]: every stored slot is a named variant's */ + { /* slots */ + int rides = 0; int32_t i; + for ( i = 0; i < 4; i++ ) { if ( !( value->slots[i] == 0 ) ) { rides = 1; break; } } + if ( rides ) { - if ( value->teams[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xe68c2e6bb1ee5646ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - case TEAM_GOLD: key_id = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_padded_row_wire_slots_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - (void) key_id; - pairs_teams++; + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_padded_row_wire_slots_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_padded_row_wire_slots_( w, value ) ) { return 0; } + } + } + } + { /* teams */ + int rides = 0; int32_t i; + for ( i = 0; i < TEAM_MAX; i++ ) + { + if ( value->teams[i] == 0 ) { continue; } + rides = 1; break; } - if ( pairs_teams > 0 ) + if ( rides ) { - int64_t len_at_teams; - /* KIND 16, not 14: a keyed body and a positional one are - incompatible, so a reader of the other kind must see a kind - mismatch and skip, never misdecode (docs/SPEC-TABLES.md §3.2) */ - table_writer_put16( w, 0x9ae1 ); table_writer_put8( w, 16 ); /* teams (keyed by Team) */ - len_at_teams = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 6 ); table_writer_put32( w, pairs_teams ); - /* ASCENDING BY VARIANT ORDINAL, which is slot order — this - writer's choice, and a reader must not rely on it: every - slot is found by its key (docs/SPEC-TABLES.md §3.2) */ - for ( i = 0; i < TEAM_MAX; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbaaeb048a5a8fa6dull ); table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at_teams; - if ( value->teams[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - case TEAM_GOLD: key_id = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - table_writer_put16( w, key_id ); /* the slot's VARIANT id, not its position */ - elem_len_at_teams = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, (uint8_t) ( value->teams[i] ) ); - table_writer_patch32( w, elem_len_at_teams, (uint32_t) ( w->offset - elem_len_at_teams - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_padded_row_wire_teams_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_padded_row_wire_teams_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_padded_row_wire_teams_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_teams, (uint32_t) ( w->offset - len_at_teams - 4 ) ); } } - if ( value->counter_present ) /* ?int32 */ - { - table_writer_put16( w, 0x428f ); table_writer_put8( w, 4 ); /* counter */ - table_writer_put32( w, (uint32_t) ( value->counter ) ); + { /* counter */ + if ( value->counter_present ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x77976c7416517c63ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->counter ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t padded_row_measure( const PaddedRow * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !padded_row_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t padded_row_save( const PaddedRow * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !padded_row_save_body( &w, value ) ) { return -1; } - return w.offset; /* == padded_row_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_load_body( TableReader * r, PaddedRow * value ) +static SCHEMA_UNUSED int padded_row_load_body( TableReader * r, PaddedRow * value ) { - padded_row_reset( value ); /* prefill declared defaults in place, then overlay */ + padded_row_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xbc64: /* tag */ + case 0x56d7ab194448a4f3ull: /* tag */ { if ( kind != 6 ) { @@ -822,26 +1625,71 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->tag = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->tag = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x9194: /* value */ + case 0x7ce4fd9430e80ceaull: /* value */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->value = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->value = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->value = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->value = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->value = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x153d: /* flag */ + case 0xd5f2d079088c0b17ull: /* flag */ { if ( kind != 1 ) { @@ -849,26 +1697,88 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->flag = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->flag = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x5dd8: /* id */ + case 0x08b72e07b55c3ac0ull: /* id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe16a: /* label */ + case 0x39f7fcec8fcb623dull: /* label */ { if ( kind != 12 ) { @@ -876,20 +1786,18 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 15 ) { keep = 15; r->report->clamped++; } - memcpy( value->label, r->buffer + r->offset, keep ); - value->label[keep] = 0; - value->label_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->label[0] = 0; value->label_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 15 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 15 ); r->report->clamped++; } + memcpy( value->label, sub.buffer, (size_t) keep ); value->label[keep] = 0; value->label_length = (int32_t) keep; break; } - case 0xf4d8: /* slots */ + case 0xe68c2e6bb1ee5646ull: /* slots */ { if ( kind != 14 ) { @@ -897,40 +1805,49 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - if ( elem_kind != 7 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 7 ) + { + if ( !(table_kind_widens(elem_kind,7)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + for ( i = 0; i < kept; i++ ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &sub ); - value->slots[i] = decoded_v; + switch ( elem_kind ) + { + case 6: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_slots; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &sub ); + value->slots[i] = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; goto end_slots; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &sub ); + value->slots[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_slots; + } } + end_slots: ; } } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x9ae1: /* teams */ + case 0xbaaeb048a5a8fa6dull: /* teams */ { if ( kind != 16 ) { @@ -938,194 +1855,649 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_row_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - TableReader sub; - uint32_t i; - if ( elem_kind != 6 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); + uint8_t elem_kind; uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 6 ) { if ( !(table_kind_widens(elem_kind,6)) ) { r->report->kind_mismatch++; break; } r->report->widened++; } for ( i = 0; i < count; i++ ) { - uint16_t key; - uint32_t elem_len; - Team slot; - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - key = table_reader_get16( &sub ); - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - if ( key == 0 ) + uint64_t key_ref, key; int32_t slot = -1; TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) { - /* None is the NULL KEY: 0 is the reserved id no declared - name can fold to, so a body carrying one is DAMAGED, not - merely foreign. Framing damage stops this body, keeps what - it decoded, and the parent reads on past the length - (docs/SPEC-TABLES.md §3.2, §4). */ - r->report->malformed = 1; - break; + case 0x9ff1de19feac1b7cull: slot = 0; break; + case 0xecf3d3a7c1693e2dull: slot = 1; break; + case 0xcf00d78fd5953f1cull: slot = 2; break; + case 0xc416c97e0d3218b3ull: slot = 3; break; + default: r->report->unknown++; break; } - slot = TEAM_NONE; + if ( slot < 0 ) { continue; } + switch ( elem_kind ) { - int32_t before = r->report->unknown; - switch ( key ) - { - case 0: slot = TEAM_NONE; break; - case 0xbb03: slot = TEAM_RED; break; - case 0xf630: slot = TEAM_BLUE; break; - case 0x6e0f: slot = TEAM_GREEN; break; - case 0xda27: slot = TEAM_GOLD; break; - default: slot = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ - } - if ( r->report->unknown != before ) + case 6: { - sub.offset += elem_len; /* a slot this reader cannot name */ - continue; + if ( !table_reader_has( &elem, 1 ) ) { r->report->malformed = 1; goto next_teams; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &elem ); + value->teams[slot] = decoded_v; + } + break; } + default: r->report->malformed = 1; goto next_teams; } - { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - if ( !table_reader_has( &elem, 1 ) ) { r->report->malformed = 1; sub.offset += elem_len; continue; } - { - uint8_t decoded_v = (uint8_t) table_reader_get8( &elem ); - value->teams[(int32_t) slot - 1] = decoded_v; - } - } - sub.offset += elem_len; + next_teams: ; } } - r->offset = body_end; /* unread pairs and slack skip via the length */ break; } - case 0x428f: /* counter */ + case 0x77976c7416517c63ull: /* counter */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->counter = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->counter = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + value->counter = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + value->counter_present = 1; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - value->counter = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->counter = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->counter = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + value->counter = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } value->counter_present = 1; break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int padded_row_load( PaddedRow * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + padded_row_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return padded_row_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t padded_frame_measure( const PaddedFrame * value ) +static SCHEMA_UNUSED int padded_row_save_message_body(TableBitWriter * w,const PaddedRow * value) { - int64_t bytes = 2; /* terminator */ - if ( value->marker != 0 ) { bytes += 3 + 1; } /* marker */ - if ( value->stamp != 0 ) { bytes += 3 + 8; } /* stamp */ - if ( value->rows_count < 0 || value->rows_count > 64 ) { return -1; } /* storage invariant */ - if ( value->rows_count > 0 ) + (void)value; + { /* tag */ + if(!(value->tag == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,5,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->tag)-UINT64_C(0),8); + } + } + { /* value */ + if(!(value->value == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,6,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->value),64); + } + } + { /* flag */ + if(!(value->flag == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,7,kTableMessageRefBitsHere); + table_bit_put(w,value->flag ? 1 : 0,1); + } + } + { /* id */ + if(!(value->id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,8,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->id)-UINT64_C(0),32); + } + } + { /* label */ + if(value->label_length<0 || value->label_length>15) return 0; + if(value->label_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,9,kTableMessageRefBitsHere); + if(value->label_length<0 || value->label_length>15) return 0; + table_bit_put(w,(uint64_t)value->label_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->label,value->label_length); + } + } + { /* slots */ + int rides=0; + { int32_t i; + for(i=0;i<4;i++) if(!(value->slots[i] == 0)) { rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,10,kTableMessageRefBitsHere); + if(4<0 || 4>4) return 0; + table_bit_put(w,(uint64_t)4-4,0); + { int32_t i; + for(i=0;i<4;i++) { + table_bit_put(w,(uint64_t)(value->slots[i])-UINT64_C(0),16); + } } + } + } + { /* teams */ + int rides=0; + { int32_t i; + for(i=0;i<4;i++) { + if(value->teams[i] == 0) continue; + rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,11,kTableMessageRefBitsHere); + { uint32_t count=0; + int32_t i; + for(i=0;i<4;i++) { + if(value->teams[i] == 0) continue; + count++; + } + table_bit_put(w,count,3); + for(i=0;i<4;i++) { + if(value->teams[i] == 0) continue; + switch(i+1) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + table_bit_put(w,(uint64_t)(value->teams[i])-UINT64_C(0),8); + } } + } + } + { /* counter */ + if(value->counter_present) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,12,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->counter)-UINT64_C(0),32); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t padded_row_measure_messages(const PaddedRow * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x56d7ab194448a4f3): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->tag=(uint8_t)decoded; + } + break; + } + case UINT64_C(0x7ce4fd9430e80cea): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->value=decoded; + } + break; + } + case UINT64_C(0xd5f2d079088c0b17): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->flag=lo!=0; + } + break; + } + case UINT64_C(0x08b72e07b55c3ac0): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x39f7fcec8fcb623d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,15); + memcpy(value->label,text,(size_t)kept); + value->label[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->label_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xe68c2e6bb1ee5646): { + if(!(entry->kind==14 && entry->elem_kind==7)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;islots[i] : &scratch; + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>65535ull){decoded=65535ull; + r->report->clamped++; + } + (*item)=(uint16_t)decoded; + } + }if(walkvalue_bits))goto malformed; + } + break; + } + case UINT64_C(0xbaaeb048a5a8fa6d): { + if(!(entry->kind==16 && entry->elem_kind==6)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0x9ff1de19feac1b7c):slot=0; + break; + case UINT64_C(0xecf3d3a7c1693e2d):slot=1; + break; + case UINT64_C(0xcf00d78fd5953f1c):slot=2; + break; + case UINT64_C(0xc416c97e0d3218b3):slot=3; + break; + default:break; + } if(slot<0){r->report->unknown++; + { uint8_t scratch; + memset(&scratch,0,sizeof(scratch)); + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + scratch=(uint8_t)decoded; + } + }continue; + } + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->teams[slot]=(uint8_t)decoded; + } + } } + break; + } + case UINT64_C(0x77976c7416517c63): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2147483648ll){decoded=-2147483648ll; + r->report->clamped++; + } + if(decoded>2147483647ll){decoded=2147483647ll; + r->report->clamped++; + } + value->counter=(int32_t)decoded; + } + value->counter_present=1; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int padded_row_load_messages(PaddedRow * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_blockdemo_padded_frame_wire_rows_( TableWriter * w, const PaddedFrame * value ) +{ + if ( value->rows_count < 0 || value->rows_count > 64 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->rows_count ); + for ( i = 0; i < value->rows_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* rows */ - for ( i = 0; i < value->rows_count; i++ ) + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !padded_row_save_body( w, &value->rows[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_rows = padded_row_measure( &value->rows[i] ); - if ( elem_rows < 0 ) { return -1; } - bytes += 4 + elem_rows; + TableWriter probe = table_writer_probe( w ); + if ( !padded_row_save_body( &probe, &value->rows[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !padded_row_save_body( w, &value->rows[i] ) ) { return 0; } } } - if ( value->blob_length < 0 || value->blob_length > 12 ) { return -1; } /* storage invariant */ - if ( value->blob_length > 0 ) { bytes += 3 + 4 + 5 + value->blob_length; } /* blob */ - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_frame_save_body( TableWriter * w, const PaddedFrame * value ) +static SCHEMA_UNUSED int schema_blockdemo_padded_frame_wire_blob_( TableWriter * w, const PaddedFrame * value ) { - if ( value->marker != 0 ) - { - table_writer_put16( w, 0x866f ); table_writer_put8( w, 6 ); /* marker */ - table_writer_put8( w, (uint8_t) ( value->marker ) ); + if ( value->blob_length < 0 || value->blob_length > 12 ) { return 0; } + table_writer_put8( w, 6 ); table_writer_leb( w, (uint64_t) value->blob_length ); + table_writer_raw( w, value->blob, value->blob_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int padded_frame_save_body( TableWriter * w, const PaddedFrame * value ) +{ + (void) value; + { /* marker */ + if ( !( value->marker == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xeddcb72b15486e77ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->marker ) ); + } } - if ( value->stamp != 0 ) - { - table_writer_put16( w, 0x0dc6 ); table_writer_put8( w, 9 ); /* stamp */ - table_writer_put64( w, (uint64_t) ( value->stamp ) ); + { /* stamp */ + if ( !( value->stamp == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xee7ba9ad45c64144ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->stamp ) ); + } } - if ( value->rows_count < 0 || value->rows_count > 64 ) { return 0; } /* storage invariant */ - if ( value->rows_count > 0 ) - { - int64_t len_at_rows; - int32_t i; - table_writer_put16( w, 0x99af ); table_writer_put8( w, 14 ); /* rows */ - len_at_rows = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->rows_count ); - for ( i = 0; i < value->rows_count; i++ ) + { /* rows */ + if ( value->rows_count < 0 || value->rows_count > 64 ) { return 0; } + if ( value->rows_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa3a7061ff10a8138ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !padded_row_save_body( w, &value->rows[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_padded_frame_wire_rows_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_padded_frame_wire_rows_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_padded_frame_wire_rows_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_rows, (uint32_t) ( w->offset - len_at_rows - 4 ) ); } - if ( value->blob_length < 0 || value->blob_length > 12 ) { return 0; } /* storage invariant */ - if ( value->blob_length > 0 ) - { - table_writer_put16( w, 0xd316 ); table_writer_put8( w, 14 ); /* blob */ - table_writer_put32( w, (uint32_t) ( 5 + value->blob_length ) ); - table_writer_put8( w, 6 ); table_writer_put32( w, (uint32_t) value->blob_length ); - table_writer_raw( w, value->blob, value->blob_length ); + { /* blob */ + if ( value->blob_length < 0 || value->blob_length > 12 ) { return 0; } + if ( value->blob_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc573b39bc29148caull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_padded_frame_wire_blob_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_padded_frame_wire_blob_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_padded_frame_wire_blob_( w, value ) ) { return 0; } + } + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t padded_frame_measure( const PaddedFrame * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !padded_frame_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t padded_frame_save( const PaddedFrame * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !padded_frame_save_body( &w, value ) ) { return -1; } - return w.offset; /* == padded_frame_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_frame_load_body( TableReader * r, PaddedFrame * value ) +static SCHEMA_UNUSED int padded_frame_load_body( TableReader * r, PaddedFrame * value ) { - padded_frame_reset( value ); /* prefill declared defaults in place, then overlay */ + padded_frame_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x866f: /* marker */ + case 0xeddcb72b15486e77ull: /* marker */ { if ( kind != 6 ) { @@ -1133,29 +2505,105 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->marker = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->marker = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x0dc6: /* stamp */ + case 0xee7ba9ad45c64144ull: /* stamp */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->stamp = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->stamp = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->stamp = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->stamp = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->stamp = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->stamp = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->stamp = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->stamp = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->stamp = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x99af: /* rows */ + case 0xa3a7061ff10a8138ull: /* rows */ { if ( kind != 14 ) { @@ -1163,47 +2611,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 64 ) { keep = 64; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) + { + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 64 ) { kept = 64; r->report->clamped++; } + int32_t previous_count = value->rows_count; + value->rows_count = 0; + for ( i = 0; i < kept; i++ ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_rows; } padded_row_load_body( &elem, &value->rows[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; padded_row_reset(&value->rows[i]); } + value->rows_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_rows: ; + { uint32_t tail; for (tail=(uint32_t)value->rows_count;tail<(uint32_t)previous_count && tail<64;tail++) { + padded_row_reset(&value->rows[tail]); + } } } - value->rows_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0xd316: /* blob */ + case 0xc573b39bc29148caull: /* blob */ { if ( kind != 14 ) { @@ -1211,61 +2651,280 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int padded_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 6 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 12 ) { keep = 12; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 6 ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); - value->blob[i] = decoded_v; + if ( !(table_kind_widens(elem_kind,6)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 12 ) { kept = 12; r->report->clamped++; } + value->blob_length = 0; + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 6: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_blob; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); + value->blob[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_blob; + } + value->blob_length = (int32_t) i + 1; } - decoded = i + 1; + end_blob: ; } - value->blob_length = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int padded_frame_load( PaddedFrame * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + padded_frame_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return padded_frame_load_body( &r, value ); } +static SCHEMA_UNUSED int padded_frame_save_message_body(TableBitWriter * w,const PaddedFrame * value) +{ + (void)value; + { /* marker */ + if(!(value->marker == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,1,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->marker)-UINT64_C(0),8); + } + } + { /* stamp */ + if(!(value->stamp == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,2,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->stamp)-UINT64_C(0),64); + } + } + { /* rows */ + if(value->rows_count<0 || value->rows_count>64) return 0; + if(value->rows_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,3,kTableMessageRefBitsHere); + if(value->rows_count<0 || value->rows_count>64) return 0; + table_bit_put(w,(uint64_t)value->rows_count-0,7); + { int32_t i; + for(i=0;irows_count;i++) { + if(!padded_row_save_message_body(w,&value->rows[i])) return 0; + } } + } + } + { /* blob */ + if(value->blob_length<0 || value->blob_length>12) return 0; + if(value->blob_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,4,kTableMessageRefBitsHere); + if(value->blob_length<0 || value->blob_length>12) return 0; + table_bit_put(w,(uint64_t)value->blob_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->blob,value->blob_length); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t padded_frame_measure_messages(const PaddedFrame * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xeddcb72b15486e77): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->marker=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xee7ba9ad45c64144): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->stamp=(uint64_t)decoded; + } + break; + } + case UINT64_C(0xa3a7061ff10a8138): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>64?64:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;irows[i] : &scratch; + if(!padded_row_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->rows_count=(int32_t)kept; + } + break; + } + case UINT64_C(0xc573b39bc29148ca): { + if(!(entry->kind==14 && entry->elem_kind==6)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,14)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(entry->min,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + kept=n>12 ? 12 : n; + if(!table_bit_getbytes(&r->bits,value->blob,(int64_t)kept)||!table_message_skip_run(r,n-kept,8))goto malformed; + if(keptreport->clamped++; + value->blob_length=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int padded_frame_load_messages(PaddedFrame * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define padded_row_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define padded_row_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define padded_row_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define padded_row_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define padded_row_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define padded_frame_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define padded_frame_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define padded_frame_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define padded_frame_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define padded_frame_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* padded_row_open: match the header and POINT. On a match the bytes ARE what this @@ -1287,6 +2946,8 @@ static SCHEMA_UNUSED int padded_frame_load( PaddedFrame * value, const uint8_t * file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const PaddedRow * padded_row_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const PaddedRow *)table_cook_open_ex(bytes,length,sizeof(PaddedRow),SCHEMA_TABLE_ALIGNOF(PaddedRow),reason); } static SCHEMA_UNUSED const PaddedRow * padded_row_open( const void * bytes, uint64_t length ) { return (const PaddedRow *) table_cook_open( bytes, length, (uint64_t) sizeof( PaddedRow ), (uint64_t) SCHEMA_TABLE_ALIGNOF( PaddedRow ) ); @@ -1311,11 +2972,67 @@ static SCHEMA_UNUSED const PaddedRow * padded_row_open( const void * bytes, uint file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const PaddedFrame * padded_frame_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const PaddedFrame *)table_cook_open_ex(bytes,length,sizeof(PaddedFrame),SCHEMA_TABLE_ALIGNOF(PaddedFrame),reason); } static SCHEMA_UNUSED const PaddedFrame * padded_frame_open( const void * bytes, uint64_t length ) { return (const PaddedFrame *) table_cook_open( bytes, length, (uint64_t) sizeof( PaddedFrame ), (uint64_t) SCHEMA_TABLE_ALIGNOF( PaddedFrame ) ); } +static SCHEMA_UNUSED int schema_blockdemo_padded_row_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const PaddedRow * value=(const PaddedRow *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->tag,1,order); + { uint64_t bits; memcpy(&bits,&value->value,8); table_cook_put(at+8,bits,8,order); } + table_cook_put(at+16,value->flag ? 1 : 0,1,order); + table_cook_put(at+20,(uint64_t)value->id,4,order); + table_cook_bytes(at+24,value->label,value->label_length,16); + table_cook_put(at+24+16,(uint32_t)value->label_length,4,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<4;cook_i1++) { + table_cook_put(at+44+cook_i1*2,(uint64_t)value->slots[cook_i1],2,order); + } } + { int32_t cook_i2; for(cook_i2=0;cook_i2<4;cook_i2++) { + table_cook_put(at+52+cook_i2*1,(uint64_t)value->teams[cook_i2],1,order); + } } + table_cook_put(at+56+4,value->counter_present ? 1 : 0,1,order); + table_cook_put(at+56,(uint64_t)value->counter,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t padded_row_cook_measure(const PaddedRow * value) { (void)value; return 144; } +static SCHEMA_UNUSED int padded_row_cook(const PaddedRow * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<144) { return 0; } + memset(raw,0,144); + if(!schema_blockdemo_padded_row_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+136,UINT64_C(0x1d3a1648d6207581),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,64,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_padded_frame_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const PaddedFrame * value=(const PaddedFrame *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->marker,1,order); + table_cook_put(at+8,(uint64_t)value->stamp,8,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<64;cook_i1++) { + if(!schema_blockdemo_padded_row_cook_body_(n,at+16+cook_i1*64,&value->rows[cook_i1],order)) { return 0; } + } } + table_cook_put(at+16+4096,(uint32_t)value->rows_count,4,order); + table_cook_bytes(at+4116,value->blob,value->blob_length,12); + table_cook_put(at+4116+12,(uint32_t)value->blob_length,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t padded_frame_cook_measure(const PaddedFrame * value) { (void)value; return 4216; } +static SCHEMA_UNUSED int padded_frame_cook(const PaddedFrame * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<4216) { return 0; } + memset(raw,0,4216); + if(!schema_blockdemo_padded_frame_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+4208,UINT64_C(0xbf050ec6de1ec3e0),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,4136,1,8,order); return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/block-c/RenderTable.c b/testdata/golden/tables/block-c/RenderTable.c index b9f3f664f..1b04a5a92 100644 --- a/testdata/golden/tables/block-c/RenderTable.c +++ b/testdata/golden/tables/block-c/RenderTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else - { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } - } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,12 +2330,12 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableFieldInfo schema_blockdemo_render_camera_fields_[] = { - { "position", "position", "RenderVector3", 0xdd45, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, position ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "rotation", "rotation", "RenderQuaternion", 0x60f3, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, rotation ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "camera_id", "camera_id", "uint32", 0x25bd, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, camera_id ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->camera_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "camera_type", "camera_type", "uint32", 0x9920, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, camera_type ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->camera_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "target_object_id", "target_object_id", "uint32", 0x38dc, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, target_object_id ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->target_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "fov", "fov", "float32", 0x392f, 10, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, fov ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->fov ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "position", "position", "RenderVector3", 0x4cbf3a26fca1d74aull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, position ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", "rotation", "RenderQuaternion", 0xb51afb05cd34709full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, rotation ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "camera_id", "camera_id", "uint32", 0x9f61700f084ab92eull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, camera_id ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->camera_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "camera_type", "camera_type", "uint32", 0x336d3dc7fffd0c9bull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, camera_type ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->camera_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "target_object_id", "target_object_id", "uint32", 0x6a0c6a156952b9c2ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, target_object_id ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->target_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "fov", "fov", "float32", 0xdcb27c18fed9e15cull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RenderCamera, fov ), (uint32_t) sizeof( ( (RenderCamera *) 0 )->fov ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_camera_info_ = { "RenderCamera", (uint32_t) sizeof( RenderCamera ), 6, schema_blockdemo_render_camera_fields_, schema_blockdemo_render_camera_reset_raw_, TableDocNone, 0, NULL }; @@ -1746,230 +2347,230 @@ static const TableVariantInfo schema_blockdemo_render_ship_flags_variants_[] = { { "Aiming", 0 }, }; static const TableVariantInfo schema_blockdemo_render_ship_ship_type_variants_[] = { - { "None", 0x0000 }, - { "Fighter", 0x412b }, - { "Bomber", 0xb7ce }, - { "Freighter", 0xb617 }, + { "None", 0x0000000000000000ull }, + { "Fighter", 0xd011f7c3c15285c2ull }, + { "Bomber", 0xa8216aa1c554cb8aull }, + { "Freighter", 0x6c2321a3d00e23dbull }, }; static const TableVariantInfo schema_blockdemo_render_ship_team_variants_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_render_ship_fields_[] = { - { "position", "position", "RenderVector3", 0xdd45, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, position ), (uint32_t) sizeof( ( (RenderShip *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "rotation", "rotation", "RenderQuaternion", 0x60f3, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, rotation ), (uint32_t) sizeof( ( (RenderShip *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "flags", "flags", "ShipFlags", 0xe64b, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, flags ), (uint32_t) sizeof( ( (RenderShip *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_blockdemo_render_ship_flags_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "object_id", "object_id", "uint32", 0xdc71, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, object_id ), (uint32_t) sizeof( ( (RenderShip *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "target_object_id", "target_object_id", "uint32", 0x38dc, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, target_object_id ), (uint32_t) sizeof( ( (RenderShip *) 0 )->target_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "thrust", "thrust", "float32", 0x9e51, 10, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, thrust ), (uint32_t) sizeof( ( (RenderShip *) 0 )->thrust ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "object_sequence", "object_sequence", "uint8", 0x57ee, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, object_sequence ), (uint32_t) sizeof( ( (RenderShip *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "ship_type", "ship_type", "ShipType", 0x38e9, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, ship_type ), (uint32_t) sizeof( ( (RenderShip *) 0 )->ship_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_blockdemo_render_ship_ship_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "team", "team", "Team", 0xdff1, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, team ), (uint32_t) sizeof( ( (RenderShip *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 4, schema_blockdemo_render_ship_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "has_target_lock", "has_target_lock", "bool", 0xf223, 1, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, has_target_lock ), (uint32_t) sizeof( ( (RenderShip *) 0 )->has_target_lock ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "predicted_explode", "predicted_explode", "bool", 0x88bd, 1, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, predicted_explode ), (uint32_t) sizeof( ( (RenderShip *) 0 )->predicted_explode ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "position", "position", "RenderVector3", 0x4cbf3a26fca1d74aull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, position ), (uint32_t) sizeof( ( (RenderShip *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", "rotation", "RenderQuaternion", 0xb51afb05cd34709full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, rotation ), (uint32_t) sizeof( ( (RenderShip *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", "flags", "ShipFlags", 0x17a3a1a985f75aecull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, flags ), (uint32_t) sizeof( ( (RenderShip *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_blockdemo_render_ship_flags_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_id", "object_id", "uint32", 0x0bdab42c07f19812ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, object_id ), (uint32_t) sizeof( ( (RenderShip *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "target_object_id", "target_object_id", "uint32", 0x6a0c6a156952b9c2ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, target_object_id ), (uint32_t) sizeof( ( (RenderShip *) 0 )->target_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "thrust", "thrust", "float32", 0xcfd587cb3100cd73ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, thrust ), (uint32_t) sizeof( ( (RenderShip *) 0 )->thrust ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_sequence", "object_sequence", "uint8", 0x5de0015285763c46ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, object_sequence ), (uint32_t) sizeof( ( (RenderShip *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ship_type", "ship_type", "ShipType", 0x1f7d2e86a2e77268ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, ship_type ), (uint32_t) sizeof( ( (RenderShip *) 0 )->ship_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_blockdemo_render_ship_ship_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", "team", "Team", 0xfa23d9ef19afc32cull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, team ), (uint32_t) sizeof( ( (RenderShip *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_blockdemo_render_ship_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_target_lock", "has_target_lock", "bool", 0x1554fa45a1220171ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, has_target_lock ), (uint32_t) sizeof( ( (RenderShip *) 0 )->has_target_lock ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "predicted_explode", "predicted_explode", "bool", 0x4d5be97ba1b9cb81ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RenderShip, predicted_explode ), (uint32_t) sizeof( ( (RenderShip *) 0 )->predicted_explode ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_ship_info_ = { "RenderShip", (uint32_t) sizeof( RenderShip ), 11, schema_blockdemo_render_ship_fields_, schema_blockdemo_render_ship_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_blockdemo_render_turret_team_variants_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_render_turret_fields_[] = { - { "rotation", "rotation", "RenderQuaternion", 0x60f3, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, rotation ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "flags", "flags", "uint64", 0xe64b, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, flags ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "object_id", "object_id", "uint32", 0xdc71, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, object_id ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "parent_object_id", "parent_object_id", "uint32", 0xeeb6, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, parent_object_id ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->parent_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "turret_index", "turret_index", "uint32", 0xae10, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, turret_index ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->turret_index ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "target_object_id", "target_object_id", "uint32", 0x38dc, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, target_object_id ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->target_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "object_sequence", "object_sequence", "uint8", 0x57ee, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, object_sequence ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "team", "team", "Team", 0xdff1, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, team ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 4, schema_blockdemo_render_turret_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "has_target_lock", "has_target_lock", "bool", 0xf223, 1, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, has_target_lock ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->has_target_lock ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "rotation", "rotation", "RenderQuaternion", 0xb51afb05cd34709full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, rotation ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", "flags", "uint64", 0x17a3a1a985f75aecull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, flags ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_id", "object_id", "uint32", 0x0bdab42c07f19812ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, object_id ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "parent_object_id", "parent_object_id", "uint32", 0x0bee7b3cb4046c93ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, parent_object_id ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->parent_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "turret_index", "turret_index", "uint32", 0x88a11a6aed541f54ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, turret_index ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->turret_index ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "target_object_id", "target_object_id", "uint32", 0x6a0c6a156952b9c2ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, target_object_id ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->target_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_sequence", "object_sequence", "uint8", 0x5de0015285763c46ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, object_sequence ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", "team", "Team", 0xfa23d9ef19afc32cull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, team ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_blockdemo_render_turret_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_target_lock", "has_target_lock", "bool", 0x1554fa45a1220171ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( RenderTurret, has_target_lock ), (uint32_t) sizeof( ( (RenderTurret *) 0 )->has_target_lock ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_turret_info_ = { "RenderTurret", (uint32_t) sizeof( RenderTurret ), 9, schema_blockdemo_render_turret_fields_, schema_blockdemo_render_turret_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_blockdemo_render_missile_missile_type_variants_[] = { - { "None", 0x0000 }, - { "Seeker", 0xf8ff }, - { "Dumb", 0xd533 }, + { "None", 0x0000000000000000ull }, + { "Seeker", 0xf5d0a4d25a76afcaull }, + { "Dumb", 0x478d7972f6d9075dull }, }; static const TableVariantInfo schema_blockdemo_render_missile_team_variants_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_render_missile_fields_[] = { - { "position", "position", "RenderVector3", 0xdd45, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, position ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "rotation", "rotation", "RenderQuaternion", 0x60f3, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, rotation ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "flags", "flags", "uint64", 0xe64b, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, flags ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "object_id", "object_id", "uint32", 0xdc71, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, object_id ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "object_sequence", "object_sequence", "uint8", 0x57ee, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, object_sequence ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "missile_type", "missile_type", "MissileType", 0x423a, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, missile_type ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->missile_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 2, schema_blockdemo_render_missile_missile_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "team", "team", "Team", 0xdff1, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, team ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 4, schema_blockdemo_render_missile_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "position", "position", "RenderVector3", 0x4cbf3a26fca1d74aull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, position ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", "rotation", "RenderQuaternion", 0xb51afb05cd34709full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, rotation ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", "flags", "uint64", 0x17a3a1a985f75aecull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, flags ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_id", "object_id", "uint32", 0x0bdab42c07f19812ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, object_id ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_sequence", "object_sequence", "uint8", 0x5de0015285763c46ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, object_sequence ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "missile_type", "missile_type", "MissileType", 0x151b2a0e2c07b4bcull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, missile_type ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->missile_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_blockdemo_render_missile_missile_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", "team", "Team", 0xfa23d9ef19afc32cull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderMissile, team ), (uint32_t) sizeof( ( (RenderMissile *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_blockdemo_render_missile_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_missile_info_ = { "RenderMissile", (uint32_t) sizeof( RenderMissile ), 7, schema_blockdemo_render_missile_fields_, schema_blockdemo_render_missile_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_blockdemo_render_dynamic_prop_prop_type_variants_[] = { - { "None", 0x0000 }, - { "Rock", 0x2c93 }, - { "Station", 0x4532 }, - { "Beacon", 0x4b61 }, + { "None", 0x0000000000000000ull }, + { "Rock", 0xcaa6172bef74e234ull }, + { "Station", 0x4f98acac2852d653ull }, + { "Beacon", 0x7f3a7e4744ea3019ull }, }; static const TableVariantInfo schema_blockdemo_render_dynamic_prop_team_variants_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_render_dynamic_prop_fields_[] = { - { "position", "position", "RenderVector3", 0xdd45, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, position ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "rotation", "rotation", "RenderQuaternion", 0x60f3, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, rotation ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "flags", "flags", "uint64", 0xe64b, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, flags ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "object_id", "object_id", "uint32", 0xdc71, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, object_id ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "object_sequence", "object_sequence", "uint8", 0x57ee, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, object_sequence ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "prop_type", "prop_type", "PropType", 0xe338, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, prop_type ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->prop_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_blockdemo_render_dynamic_prop_prop_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "team", "team", "Team", 0xdff1, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, team ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 4, schema_blockdemo_render_dynamic_prop_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "position", "position", "RenderVector3", 0x4cbf3a26fca1d74aull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, position ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", "rotation", "RenderQuaternion", 0xb51afb05cd34709full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, rotation ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", "flags", "uint64", 0x17a3a1a985f75aecull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, flags ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_id", "object_id", "uint32", 0x0bdab42c07f19812ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, object_id ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "object_sequence", "object_sequence", "uint8", 0x5de0015285763c46ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, object_sequence ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->object_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "prop_type", "prop_type", "PropType", 0xe5626f155e4c5dadull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, prop_type ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->prop_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_blockdemo_render_dynamic_prop_prop_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", "team", "Team", 0xfa23d9ef19afc32cull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderDynamicProp, team ), (uint32_t) sizeof( ( (RenderDynamicProp *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_blockdemo_render_dynamic_prop_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_dynamic_prop_info_ = { "RenderDynamicProp", (uint32_t) sizeof( RenderDynamicProp ), 7, schema_blockdemo_render_dynamic_prop_fields_, schema_blockdemo_render_dynamic_prop_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_blockdemo_render_static_prop_prop_type_variants_[] = { - { "None", 0x0000 }, - { "Rock", 0x2c93 }, - { "Station", 0x4532 }, - { "Beacon", 0x4b61 }, + { "None", 0x0000000000000000ull }, + { "Rock", 0xcaa6172bef74e234ull }, + { "Station", 0x4f98acac2852d653ull }, + { "Beacon", 0x7f3a7e4744ea3019ull }, }; static const TableVariantInfo schema_blockdemo_render_static_prop_team_variants_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_render_static_prop_fields_[] = { - { "position", "position", "RenderVector3", 0xdd45, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, position ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "rotation", "rotation", "RenderQuaternion", 0x60f3, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, rotation ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "scale", "scale", "float64", 0x9ee6, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, scale ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->scale ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "flags", "flags", "uint64", 0xe64b, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, flags ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "static_prop_id", "static_prop_id", "uint32", 0x0f74, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, static_prop_id ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->static_prop_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "prop_type", "prop_type", "PropType", 0xe338, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, prop_type ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->prop_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_blockdemo_render_static_prop_prop_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "team", "team", "Team", 0xdff1, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, team ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 4, schema_blockdemo_render_static_prop_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "position", "position", "RenderVector3", 0x4cbf3a26fca1d74aull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, position ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", "rotation", "RenderQuaternion", 0xb51afb05cd34709full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, rotation ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "scale", "scale", "float64", 0x6aacb9fbb71a1d91ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, scale ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->scale ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", "flags", "uint64", 0x17a3a1a985f75aecull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, flags ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "static_prop_id", "static_prop_id", "uint32", 0xd23da7ab574b95c3ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, static_prop_id ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->static_prop_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "prop_type", "prop_type", "PropType", 0xe5626f155e4c5dadull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, prop_type ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->prop_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_blockdemo_render_static_prop_prop_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", "team", "Team", 0xfa23d9ef19afc32cull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderStaticProp, team ), (uint32_t) sizeof( ( (RenderStaticProp *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_blockdemo_render_static_prop_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_static_prop_info_ = { "RenderStaticProp", (uint32_t) sizeof( RenderStaticProp ), 7, schema_blockdemo_render_static_prop_fields_, schema_blockdemo_render_static_prop_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_blockdemo_render_cosmetic_prop_prop_type_variants_[] = { - { "None", 0x0000 }, - { "Rock", 0x2c93 }, - { "Station", 0x4532 }, - { "Beacon", 0x4b61 }, + { "None", 0x0000000000000000ull }, + { "Rock", 0xcaa6172bef74e234ull }, + { "Station", 0x4f98acac2852d653ull }, + { "Beacon", 0x7f3a7e4744ea3019ull }, }; static const TableVariantInfo schema_blockdemo_render_cosmetic_prop_team_variants_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_render_cosmetic_prop_fields_[] = { - { "position", "position", "RenderVector3", 0xdd45, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, position ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "rotation", "rotation", "RenderQuaternion", 0x60f3, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, rotation ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "scale", "scale", "float64", 0x9ee6, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, scale ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->scale ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "flags", "flags", "uint64", 0xe64b, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, flags ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "cosmetic_prop_id", "cosmetic_prop_id", "uint32", 0x0843, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, cosmetic_prop_id ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->cosmetic_prop_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "prop_sequence", "prop_sequence", "uint8", 0x3069, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, prop_sequence ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->prop_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "prop_type", "prop_type", "PropType", 0xe338, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, prop_type ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->prop_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_blockdemo_render_cosmetic_prop_prop_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "team", "team", "Team", 0xdff1, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, team ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 4, schema_blockdemo_render_cosmetic_prop_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "position", "position", "RenderVector3", 0x4cbf3a26fca1d74aull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, position ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", "rotation", "RenderQuaternion", 0xb51afb05cd34709full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, rotation ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "scale", "scale", "float64", 0x6aacb9fbb71a1d91ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, scale ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->scale ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "flags", "flags", "uint64", 0x17a3a1a985f75aecull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, flags ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->flags ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "cosmetic_prop_id", "cosmetic_prop_id", "uint32", 0xb66e4449e56b2e26ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, cosmetic_prop_id ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->cosmetic_prop_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "prop_sequence", "prop_sequence", "uint8", 0x4d7bf73bcbddc228ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, prop_sequence ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->prop_sequence ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "prop_type", "prop_type", "PropType", 0xe5626f155e4c5dadull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, prop_type ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->prop_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_blockdemo_render_cosmetic_prop_prop_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", "team", "Team", 0xfa23d9ef19afc32cull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderCosmeticProp, team ), (uint32_t) sizeof( ( (RenderCosmeticProp *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_blockdemo_render_cosmetic_prop_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_cosmetic_prop_info_ = { "RenderCosmeticProp", (uint32_t) sizeof( RenderCosmeticProp ), 8, schema_blockdemo_render_cosmetic_prop_fields_, schema_blockdemo_render_cosmetic_prop_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_blockdemo_render_laser_laser_type_variants_[] = { - { "None", 0x0000 }, - { "Pulse", 0xc187 }, - { "Beam", 0x1f28 }, + { "None", 0x0000000000000000ull }, + { "Pulse", 0x94e8a172d8f4805eull }, + { "Beam", 0xa0745aa7967eeffaull }, }; static const TableVariantInfo schema_blockdemo_render_laser_team_variants_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_render_laser_fields_[] = { - { "start", "start", "RenderVector3", 0x61f4, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, start ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->start ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "finish", "finish", "RenderVector3", 0x2d84, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, finish ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->finish ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "t", "t", "float64", 0xccaf, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, t ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->t ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "laser_id", "laser_id", "uint32", 0xc5ca, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, laser_id ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->laser_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "laser_type", "laser_type", "LaserType", 0xc46a, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, laser_type ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->laser_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 2, schema_blockdemo_render_laser_laser_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "team", "team", "Team", 0xdff1, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, team ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 4, schema_blockdemo_render_laser_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "start", "start", "RenderVector3", 0xee5d97ad45ad251full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, start ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->start ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "finish", "finish", "RenderVector3", 0x6917a5bab91540f2ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, finish ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->finish ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "t", "t", "float64", 0xaf63e94c860202a3ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, t ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->t ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "laser_id", "laser_id", "uint32", 0xcd29c05f390294ecull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, laser_id ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->laser_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "laser_type", "laser_type", "LaserType", 0x96b593c242133841ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, laser_type ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->laser_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_blockdemo_render_laser_laser_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", "team", "Team", 0xfa23d9ef19afc32cull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderLaser, team ), (uint32_t) sizeof( ( (RenderLaser *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_blockdemo_render_laser_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_laser_info_ = { "RenderLaser", (uint32_t) sizeof( RenderLaser ), 6, schema_blockdemo_render_laser_fields_, schema_blockdemo_render_laser_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_blockdemo_render_explosion_explosion_type_variants_[] = { - { "None", 0x0000 }, - { "Small", 0x2eac }, - { "Large", 0x6edf }, + { "None", 0x0000000000000000ull }, + { "Small", 0x3d2cc8d952adebecull }, + { "Large", 0xc8736ef79380e634ull }, }; static const TableVariantInfo schema_blockdemo_render_explosion_team_variants_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableFieldInfo schema_blockdemo_render_explosion_fields_[] = { - { "position", "position", "RenderVector3", 0xdd45, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, position ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "rotation", "rotation", "RenderQuaternion", 0x60f3, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, rotation ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "t", "t", "float64", 0xccaf, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, t ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->t ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "explosion_id", "explosion_id", "uint32", 0x5be0, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, explosion_id ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->explosion_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "parent_object_id", "parent_object_id", "uint32", 0xeeb6, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, parent_object_id ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->parent_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "explosion_type", "explosion_type", "ExplosionType", 0x98fa, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, explosion_type ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->explosion_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 2, schema_blockdemo_render_explosion_explosion_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "team", "team", "Team", 0xdff1, 7, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, team ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 4, schema_blockdemo_render_explosion_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "position", "position", "RenderVector3", 0x4cbf3a26fca1d74aull, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, position ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->position ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_vector3_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "rotation", "rotation", "RenderQuaternion", 0xb51afb05cd34709full, 13, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, rotation ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->rotation ), 0xffffffffu, 0xffffffffu, &schema_blockdemo_render_quaternion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "t", "t", "float64", 0xaf63e94c860202a3ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, t ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->t ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "explosion_id", "explosion_id", "uint32", 0xfd7f3fa0b16ed778ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, explosion_id ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->explosion_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "parent_object_id", "parent_object_id", "uint32", 0x0bee7b3cb4046c93ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, parent_object_id ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->parent_object_id ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "explosion_type", "explosion_type", "ExplosionType", 0xdc89eb9cd3393be5ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, explosion_type ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->explosion_type ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_blockdemo_render_explosion_explosion_type_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "team", "team", "Team", 0xfa23d9ef19afc32cull, 30, 0, 0, 0, 0, (uint32_t) offsetof( RenderExplosion, team ), (uint32_t) sizeof( ( (RenderExplosion *) 0 )->team ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 4, schema_blockdemo_render_explosion_team_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_explosion_info_ = { "RenderExplosion", (uint32_t) sizeof( RenderExplosion ), 7, schema_blockdemo_render_explosion_fields_, schema_blockdemo_render_explosion_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_blockdemo_render_frame_fields_[] = { - { "version", "version", "uint64", 0xe8e6, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderFrame, version ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->version ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "cameras", "cameras", "RenderCamera", 0x8b0e, 13, 1, 1, 0, 1, (uint32_t) offsetof( RenderFrame, cameras ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->cameras[0] ), (uint32_t) offsetof( RenderFrame, cameras_count ), 0xffffffffu, &schema_blockdemo_render_camera_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "ships", "ships", "RenderShip", 0x2d39, 13, 1, 1, 0, 4096, (uint32_t) offsetof( RenderFrame, ships ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->ships[0] ), (uint32_t) offsetof( RenderFrame, ships_count ), 0xffffffffu, &schema_blockdemo_render_ship_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "turrets", "turrets", "RenderTurret", 0x48ad, 13, 1, 1, 0, 1024, (uint32_t) offsetof( RenderFrame, turrets ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->turrets[0] ), (uint32_t) offsetof( RenderFrame, turrets_count ), 0xffffffffu, &schema_blockdemo_render_turret_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "missiles", "missiles", "RenderMissile", 0xa532, 13, 1, 1, 0, 4096, (uint32_t) offsetof( RenderFrame, missiles ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->missiles[0] ), (uint32_t) offsetof( RenderFrame, missiles_count ), 0xffffffffu, &schema_blockdemo_render_missile_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "dynamic_props", "dynamic_props", "RenderDynamicProp", 0x810b, 13, 1, 1, 0, 4096, (uint32_t) offsetof( RenderFrame, dynamic_props ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->dynamic_props[0] ), (uint32_t) offsetof( RenderFrame, dynamic_props_count ), 0xffffffffu, &schema_blockdemo_render_dynamic_prop_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "static_props", "static_props", "RenderStaticProp", 0x55a9, 13, 1, 1, 0, 20000, (uint32_t) offsetof( RenderFrame, static_props ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->static_props[0] ), (uint32_t) offsetof( RenderFrame, static_props_count ), 0xffffffffu, &schema_blockdemo_render_static_prop_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "cosmetic_props", "cosmetic_props", "RenderCosmeticProp", 0x1142, 13, 1, 1, 0, 8192, (uint32_t) offsetof( RenderFrame, cosmetic_props ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->cosmetic_props[0] ), (uint32_t) offsetof( RenderFrame, cosmetic_props_count ), 0xffffffffu, &schema_blockdemo_render_cosmetic_prop_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "lasers", "lasers", "RenderLaser", 0x411a, 13, 1, 1, 0, 32000, (uint32_t) offsetof( RenderFrame, lasers ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->lasers[0] ), (uint32_t) offsetof( RenderFrame, lasers_count ), 0xffffffffu, &schema_blockdemo_render_laser_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "explosions", "explosions", "RenderExplosion", 0x6bfb, 13, 1, 1, 0, 32000, (uint32_t) offsetof( RenderFrame, explosions ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->explosions[0] ), (uint32_t) offsetof( RenderFrame, explosions_count ), 0xffffffffu, &schema_blockdemo_render_explosion_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "version", "version", "uint64", 0xbb62c62c9808ea37ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RenderFrame, version ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->version ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "cameras", "cameras", "RenderCamera", 0x0f9222d2ba7aa2a7ull, 13, 1, 1, 0, 1, (uint32_t) offsetof( RenderFrame, cameras ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->cameras[0] ), (uint32_t) offsetof( RenderFrame, cameras_count ), 0xffffffffu, &schema_blockdemo_render_camera_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ships", "ships", "RenderShip", 0x294a5c4913e1ad44ull, 13, 1, 1, 0, 4096, (uint32_t) offsetof( RenderFrame, ships ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->ships[0] ), (uint32_t) offsetof( RenderFrame, ships_count ), 0xffffffffu, &schema_blockdemo_render_ship_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "turrets", "turrets", "RenderTurret", 0x84f8260bc283608cull, 13, 1, 1, 0, 1024, (uint32_t) offsetof( RenderFrame, turrets ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->turrets[0] ), (uint32_t) offsetof( RenderFrame, turrets_count ), 0xffffffffu, &schema_blockdemo_render_turret_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "missiles", "missiles", "RenderMissile", 0x1c3027194b1ba4d0ull, 13, 1, 1, 0, 4096, (uint32_t) offsetof( RenderFrame, missiles ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->missiles[0] ), (uint32_t) offsetof( RenderFrame, missiles_count ), 0xffffffffu, &schema_blockdemo_render_missile_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "dynamic_props", "dynamic_props", "RenderDynamicProp", 0x43125398a9903d27ull, 13, 1, 1, 0, 4096, (uint32_t) offsetof( RenderFrame, dynamic_props ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->dynamic_props[0] ), (uint32_t) offsetof( RenderFrame, dynamic_props_count ), 0xffffffffu, &schema_blockdemo_render_dynamic_prop_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "static_props", "static_props", "RenderStaticProp", 0xc1f8d7edff7fcfd2ull, 13, 1, 1, 0, 20000, (uint32_t) offsetof( RenderFrame, static_props ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->static_props[0] ), (uint32_t) offsetof( RenderFrame, static_props_count ), 0xffffffffu, &schema_blockdemo_render_static_prop_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "cosmetic_props", "cosmetic_props", "RenderCosmeticProp", 0x33408e39f5f480ffull, 13, 1, 1, 0, 8192, (uint32_t) offsetof( RenderFrame, cosmetic_props ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->cosmetic_props[0] ), (uint32_t) offsetof( RenderFrame, cosmetic_props_count ), 0xffffffffu, &schema_blockdemo_render_cosmetic_prop_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "lasers", "lasers", "RenderLaser", 0xd982b77b3e92d16dull, 13, 1, 1, 0, 32000, (uint32_t) offsetof( RenderFrame, lasers ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->lasers[0] ), (uint32_t) offsetof( RenderFrame, lasers_count ), 0xffffffffu, &schema_blockdemo_render_laser_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "explosions", "explosions", "RenderExplosion", 0x876a8c1a85806a69ull, 13, 1, 1, 0, 32000, (uint32_t) offsetof( RenderFrame, explosions ), (uint32_t) sizeof( ( (RenderFrame *) 0 )->explosions[0] ), (uint32_t) offsetof( RenderFrame, explosions_count ), 0xffffffffu, &schema_blockdemo_render_explosion_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_frame_info_ = { "RenderFrame", (uint32_t) sizeof( RenderFrame ), 10, schema_blockdemo_render_frame_fields_, schema_blockdemo_render_frame_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_blockdemo_render_vector3_fields_[] = { - { "x", "x", "float64", 0xad8b, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderVector3, x ), (uint32_t) sizeof( ( (RenderVector3 *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "y", "y", "float64", 0xb2f8, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderVector3, y ), (uint32_t) sizeof( ( (RenderVector3 *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "z", "z", "float64", 0xaca1, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderVector3, z ), (uint32_t) sizeof( ( (RenderVector3 *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "x", "x", "float64", 0xaf63f54c86021707ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderVector3, x ), (uint32_t) sizeof( ( (RenderVector3 *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", "y", "float64", 0xaf63f44c86021554ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderVector3, y ), (uint32_t) sizeof( ( (RenderVector3 *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", "z", "float64", 0xaf63f74c86021a6dull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderVector3, z ), (uint32_t) sizeof( ( (RenderVector3 *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_vector3_info_ = { "RenderVector3", (uint32_t) sizeof( RenderVector3 ), 3, schema_blockdemo_render_vector3_fields_, schema_blockdemo_render_vector3_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_blockdemo_render_quaternion_fields_[] = { - { "x", "x", "float64", 0xad8b, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderQuaternion, x ), (uint32_t) sizeof( ( (RenderQuaternion *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "y", "y", "float64", 0xb2f8, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderQuaternion, y ), (uint32_t) sizeof( ( (RenderQuaternion *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "z", "z", "float64", 0xaca1, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderQuaternion, z ), (uint32_t) sizeof( ( (RenderQuaternion *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "w", "w", "float64", 0xcd3a, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderQuaternion, w ), (uint32_t) sizeof( ( (RenderQuaternion *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "x", "x", "float64", 0xaf63f54c86021707ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderQuaternion, x ), (uint32_t) sizeof( ( (RenderQuaternion *) 0 )->x ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "y", "y", "float64", 0xaf63f44c86021554ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderQuaternion, y ), (uint32_t) sizeof( ( (RenderQuaternion *) 0 )->y ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "z", "z", "float64", 0xaf63f74c86021a6dull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderQuaternion, z ), (uint32_t) sizeof( ( (RenderQuaternion *) 0 )->z ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "w", "w", "float64", 0xaf63ea4c86020456ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( RenderQuaternion, w ), (uint32_t) sizeof( ( (RenderQuaternion *) 0 )->w ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_blockdemo_render_quaternion_info_ = { "RenderQuaternion", (uint32_t) sizeof( RenderQuaternion ), 4, schema_blockdemo_render_quaternion_fields_, schema_blockdemo_render_quaternion_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/block-c/RenderTable.h b/testdata/golden/tables/block-c/RenderTable.h index fae45244b..27b200c92 100644 --- a/testdata/golden/tables/block-c/RenderTable.h +++ b/testdata/golden/tables/block-c/RenderTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package blockdemo — protocol id 0x802d031e1b4cdb0c (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_BLOCKDEMO_RENDERTABLE_H #define SCHEMA_BLOCKDEMO_RENDERTABLE_H @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -183,6 +194,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -210,6 +223,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -231,157 +246,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[89]; + uint32_t slots[256], positions[89]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (256-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(256-1); + } + if(v->count==89) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } +} +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) +{ + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } } - return 0; + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -391,6 +526,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_BLOCKDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_BLOCKDEMO_BUILD_VERSION #define SCHEMA_BLOCKDEMO_BUILD_VERSION @@ -502,25 +692,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -530,40 +720,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_BLOCKDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table RenderCamera — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in render_camera_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -916,35 +1687,35 @@ static SCHEMA_UNUSED void schema_blockdemo_render_quaternion_reset_raw_( void * /* ---- codecs: measure/save/load per closure member ---- */ static SCHEMA_UNUSED int64_t render_camera_measure( const RenderCamera * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_camera_save_body( TableWriter * w, const RenderCamera * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_camera_load_body( TableReader * r, RenderCamera * value ); +static SCHEMA_UNUSED int render_camera_save_body( TableWriter * w, const RenderCamera * value ); +static SCHEMA_UNUSED int render_camera_load_body( TableReader * r, RenderCamera * value ); static SCHEMA_UNUSED int64_t render_ship_measure( const RenderShip * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_save_body( TableWriter * w, const RenderShip * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_load_body( TableReader * r, RenderShip * value ); +static SCHEMA_UNUSED int render_ship_save_body( TableWriter * w, const RenderShip * value ); +static SCHEMA_UNUSED int render_ship_load_body( TableReader * r, RenderShip * value ); static SCHEMA_UNUSED int64_t render_turret_measure( const RenderTurret * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_turret_save_body( TableWriter * w, const RenderTurret * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_turret_load_body( TableReader * r, RenderTurret * value ); +static SCHEMA_UNUSED int render_turret_save_body( TableWriter * w, const RenderTurret * value ); +static SCHEMA_UNUSED int render_turret_load_body( TableReader * r, RenderTurret * value ); static SCHEMA_UNUSED int64_t render_missile_measure( const RenderMissile * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_missile_save_body( TableWriter * w, const RenderMissile * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_missile_load_body( TableReader * r, RenderMissile * value ); +static SCHEMA_UNUSED int render_missile_save_body( TableWriter * w, const RenderMissile * value ); +static SCHEMA_UNUSED int render_missile_load_body( TableReader * r, RenderMissile * value ); static SCHEMA_UNUSED int64_t render_dynamic_prop_measure( const RenderDynamicProp * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_dynamic_prop_save_body( TableWriter * w, const RenderDynamicProp * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_dynamic_prop_load_body( TableReader * r, RenderDynamicProp * value ); +static SCHEMA_UNUSED int render_dynamic_prop_save_body( TableWriter * w, const RenderDynamicProp * value ); +static SCHEMA_UNUSED int render_dynamic_prop_load_body( TableReader * r, RenderDynamicProp * value ); static SCHEMA_UNUSED int64_t render_static_prop_measure( const RenderStaticProp * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_static_prop_save_body( TableWriter * w, const RenderStaticProp * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_static_prop_load_body( TableReader * r, RenderStaticProp * value ); +static SCHEMA_UNUSED int render_static_prop_save_body( TableWriter * w, const RenderStaticProp * value ); +static SCHEMA_UNUSED int render_static_prop_load_body( TableReader * r, RenderStaticProp * value ); static SCHEMA_UNUSED int64_t render_cosmetic_prop_measure( const RenderCosmeticProp * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_cosmetic_prop_save_body( TableWriter * w, const RenderCosmeticProp * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_cosmetic_prop_load_body( TableReader * r, RenderCosmeticProp * value ); +static SCHEMA_UNUSED int render_cosmetic_prop_save_body( TableWriter * w, const RenderCosmeticProp * value ); +static SCHEMA_UNUSED int render_cosmetic_prop_load_body( TableReader * r, RenderCosmeticProp * value ); static SCHEMA_UNUSED int64_t render_laser_measure( const RenderLaser * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_laser_save_body( TableWriter * w, const RenderLaser * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_laser_load_body( TableReader * r, RenderLaser * value ); +static SCHEMA_UNUSED int render_laser_save_body( TableWriter * w, const RenderLaser * value ); +static SCHEMA_UNUSED int render_laser_load_body( TableReader * r, RenderLaser * value ); static SCHEMA_UNUSED int64_t render_explosion_measure( const RenderExplosion * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_explosion_save_body( TableWriter * w, const RenderExplosion * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_explosion_load_body( TableReader * r, RenderExplosion * value ); +static SCHEMA_UNUSED int render_explosion_save_body( TableWriter * w, const RenderExplosion * value ); +static SCHEMA_UNUSED int render_explosion_load_body( TableReader * r, RenderExplosion * value ); static SCHEMA_UNUSED int64_t render_frame_measure( const RenderFrame * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_save_body( TableWriter * w, const RenderFrame * value ); -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( TableReader * r, RenderFrame * value ); +static SCHEMA_UNUSED int render_frame_save_body( TableWriter * w, const RenderFrame * value ); +static SCHEMA_UNUSED int render_frame_load_body( TableReader * r, RenderFrame * value ); static SCHEMA_UNUSED int64_t render_vector3_measure( const RenderVector3 * value ); static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_vector3_save_body( TableWriter * w, const RenderVector3 * value ); static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_vector3_load_body( TableReader * r, RenderVector3 * value ); @@ -952,94 +1723,172 @@ static SCHEMA_UNUSED int64_t render_quaternion_measure( const RenderQuaternion * static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_quaternion_save_body( TableWriter * w, const RenderQuaternion * value ); static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_quaternion_load_body( TableReader * r, RenderQuaternion * value ); -static SCHEMA_UNUSED int64_t render_camera_measure( const RenderCamera * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return -1; } - if ( body_position > 2 ) { bytes += 3 + 4 + body_position; } /* position: all-default nested elides */ - } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return -1; } - if ( body_rotation > 2 ) { bytes += 3 + 4 + body_rotation; } /* rotation: all-default nested elides */ - } - if ( value->camera_id != 0 ) { bytes += 3 + 4; } /* camera_id */ - if ( value->camera_type != 0 ) { bytes += 3 + 4; } /* camera_type */ - if ( value->target_object_id != 0 ) { bytes += 3 + 4; } /* target_object_id */ - if ( value->fov != 0.0f ) { bytes += 3 + 4; } /* fov */ - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_camera_save_body( TableWriter * w, const RenderCamera * value ) -{ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_position > 2 ) /* all-default nested elides */ +static SCHEMA_UNUSED int schema_blockdemo_render_camera_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_ship_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_turret_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_missile_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_dynamic_prop_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_static_prop_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_cosmetic_prop_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_laser_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_explosion_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_frame_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_vector3_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_blockdemo_render_quaternion_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int render_camera_save_message_body(TableBitWriter *,const RenderCamera *); +static SCHEMA_UNUSED int render_ship_save_message_body(TableBitWriter *,const RenderShip *); +static SCHEMA_UNUSED int render_turret_save_message_body(TableBitWriter *,const RenderTurret *); +static SCHEMA_UNUSED int render_missile_save_message_body(TableBitWriter *,const RenderMissile *); +static SCHEMA_UNUSED int render_dynamic_prop_save_message_body(TableBitWriter *,const RenderDynamicProp *); +static SCHEMA_UNUSED int render_static_prop_save_message_body(TableBitWriter *,const RenderStaticProp *); +static SCHEMA_UNUSED int render_cosmetic_prop_save_message_body(TableBitWriter *,const RenderCosmeticProp *); +static SCHEMA_UNUSED int render_laser_save_message_body(TableBitWriter *,const RenderLaser *); +static SCHEMA_UNUSED int render_explosion_save_message_body(TableBitWriter *,const RenderExplosion *); +static SCHEMA_UNUSED int render_frame_save_message_body(TableBitWriter *,const RenderFrame *); +static SCHEMA_UNUSED int render_vector3_save_message_body(TableBitWriter *,const RenderVector3 *); +static SCHEMA_UNUSED int render_quaternion_save_message_body(TableBitWriter *,const RenderQuaternion *); +static SCHEMA_UNUSED int render_camera_load_message_body(TableMessageReader *,RenderCamera *); +static SCHEMA_UNUSED int render_ship_load_message_body(TableMessageReader *,RenderShip *); +static SCHEMA_UNUSED int render_turret_load_message_body(TableMessageReader *,RenderTurret *); +static SCHEMA_UNUSED int render_missile_load_message_body(TableMessageReader *,RenderMissile *); +static SCHEMA_UNUSED int render_dynamic_prop_load_message_body(TableMessageReader *,RenderDynamicProp *); +static SCHEMA_UNUSED int render_static_prop_load_message_body(TableMessageReader *,RenderStaticProp *); +static SCHEMA_UNUSED int render_cosmetic_prop_load_message_body(TableMessageReader *,RenderCosmeticProp *); +static SCHEMA_UNUSED int render_laser_load_message_body(TableMessageReader *,RenderLaser *); +static SCHEMA_UNUSED int render_explosion_load_message_body(TableMessageReader *,RenderExplosion *); +static SCHEMA_UNUSED int render_frame_load_message_body(TableMessageReader *,RenderFrame *); +static SCHEMA_UNUSED int render_vector3_load_message_body(TableMessageReader *,RenderVector3 *); +static SCHEMA_UNUSED int render_quaternion_load_message_body(TableMessageReader *,RenderQuaternion *); +static SCHEMA_UNUSED int schema_blockdemo_render_camera_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_ship_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_turret_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_missile_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_dynamic_prop_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_static_prop_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_cosmetic_prop_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_laser_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_explosion_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_frame_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_vector3_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_blockdemo_render_quaternion_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int render_camera_save_body( TableWriter * w, const RenderCamera * value ) +{ + (void) value; + { /* position */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->position ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0xdd45 ); table_writer_put8( w, 13 ); /* position */ - table_writer_put32( w, (uint32_t) body_position ); - if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4cbf3a26fca1d74aull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->position ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + } } } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_rotation > 2 ) /* all-default nested elides */ + { /* rotation */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_quaternion_save_body( &default_probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0x60f3 ); table_writer_put8( w, 13 ); /* rotation */ - table_writer_put32( w, (uint32_t) body_rotation ); - if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb51afb05cd34709full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_quaternion_save_body( &probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + } } } - if ( value->camera_id != 0 ) - { - table_writer_put16( w, 0x25bd ); table_writer_put8( w, 8 ); /* camera_id */ - table_writer_put32( w, (uint32_t) ( value->camera_id ) ); + { /* camera_id */ + if ( !( value->camera_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x9f61700f084ab92eull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->camera_id ) ); + } } - if ( value->camera_type != 0 ) - { - table_writer_put16( w, 0x9920 ); table_writer_put8( w, 8 ); /* camera_type */ - table_writer_put32( w, (uint32_t) ( value->camera_type ) ); + { /* camera_type */ + if ( !( value->camera_type == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x336d3dc7fffd0c9bull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->camera_type ) ); + } } - if ( value->target_object_id != 0 ) - { - table_writer_put16( w, 0x38dc ); table_writer_put8( w, 8 ); /* target_object_id */ - table_writer_put32( w, (uint32_t) ( value->target_object_id ) ); + { /* target_object_id */ + if ( !( value->target_object_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6a0c6a156952b9c2ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->target_object_id ) ); + } } - if ( value->fov != 0.0f ) - { - table_writer_put16( w, 0x392f ); table_writer_put8( w, 10 ); /* fov */ - table_writer_put32( w, table_float_to_bits( value->fov ) ); + { /* fov */ + if ( !( value->fov == 0.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xdcb27c18fed9e15cull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->fov ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_camera_measure( const RenderCamera * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_camera_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_camera_save( const RenderCamera * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_camera_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_camera_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_camera_load_body( TableReader * r, RenderCamera * value ) +static SCHEMA_UNUSED int render_camera_load_body( TableReader * r, RenderCamera * value ) { - render_camera_reset( value ); /* prefill declared defaults in place, then overlay */ + render_camera_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xdd45: /* position */ + case 0x4cbf3a26fca1d74aull: /* position */ { if ( kind != 13 ) { @@ -1047,18 +1896,13 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_camera_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->position ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->position ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->position ); } break; } - case 0x60f3: /* rotation */ + case 0xb51afb05cd34709full: /* rotation */ { if ( kind != 13 ) { @@ -1066,63 +1910,220 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_camera_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_quaternion_load_body( &sub, &value->rotation ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_quaternion_load_body( &sub, &value->rotation ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_quaternion_reset( &value->rotation ); } break; } - case 0x25bd: /* camera_id */ + case 0x9f61700f084ab92eull: /* camera_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->camera_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->camera_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->camera_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->camera_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->camera_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->camera_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->camera_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x9920: /* camera_type */ + case 0x336d3dc7fffd0c9bull: /* camera_type */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->camera_type = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->camera_type = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->camera_type = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->camera_type = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->camera_type = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->camera_type = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->camera_type = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x38dc: /* target_object_id */ + case 0x6a0c6a156952b9c2ull: /* target_object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->target_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->target_object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->target_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x392f: /* fov */ + case 0xdcb27c18fed9e15cull: /* fov */ { if ( kind != 10 ) { @@ -1130,193 +2131,445 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_camera_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->fov = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->fov = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_camera_load( RenderCamera * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_camera_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_camera_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_ship_measure( const RenderShip * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return -1; } - if ( body_position > 2 ) { bytes += 3 + 4 + body_position; } /* position: all-default nested elides */ +static SCHEMA_UNUSED int render_camera_save_message_body(TableBitWriter * w,const RenderCamera * value) +{ + (void)value; + { /* position */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->position)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->position)) return 0; + } + } + { /* rotation */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_quaternion_save_message_body(&probe,&value->rotation)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + if(!render_quaternion_save_message_body(w,&value->rotation)) return 0; + } + } + { /* camera_id */ + if(!(value->camera_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,15,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->camera_id)-UINT64_C(0),32); + } + } + { /* camera_type */ + if(!(value->camera_type == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,16,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->camera_type)-UINT64_C(0),32); + } + } + { /* target_object_id */ + if(!(value->target_object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,17,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->target_object_id)-UINT64_C(0),32); + } + } + { /* fov */ + if(!(value->fov == 0.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,18,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->fov),32); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_camera_measure_messages(const RenderCamera * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x4cbf3a26fca1d74a): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->position))return 0; + break; + } + case UINT64_C(0xb51afb05cd34709f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_quaternion_load_message_body(r,&value->rotation))return 0; + break; + } + case UINT64_C(0x9f61700f084ab92e): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->camera_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x336d3dc7fffd0c9b): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->camera_type=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x6a0c6a156952b9c2): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->target_object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xdcb27c18fed9e15c): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->fov=decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_camera_load_messages(RenderCamera * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_ship_save_body( TableWriter * w, const RenderShip * value ) +{ + (void) value; + { /* position */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->position ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4cbf3a26fca1d74aull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->position ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + } + } } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return -1; } - if ( body_rotation > 2 ) { bytes += 3 + 4 + body_rotation; } /* rotation: all-default nested elides */ + { /* rotation */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_quaternion_save_body( &default_probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb51afb05cd34709full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_quaternion_save_body( &probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + } + } } - if ( value->flags != 0 ) { bytes += 3 + 8; } /* flags */ - if ( value->object_id != 0 ) { bytes += 3 + 4; } /* object_id */ - if ( value->target_object_id != 0 ) { bytes += 3 + 4; } /* target_object_id */ - if ( value->thrust != 0.0f ) { bytes += 3 + 4; } /* thrust */ - if ( value->object_sequence != 0 ) { bytes += 3 + 1; } /* object_sequence */ - if ( value->ship_type != SHIP_TYPE_NONE ) - { - uint16_t id_ship_type = 0; - switch ( value->ship_type ) /* ShipType rides as the u16 hash of its VARIANT NAME (§5) */ + { /* flags */ + if ( !( value->flags == 0 ) ) { - case SHIP_TYPE_NONE: id_ship_type = 0; break; - case SHIP_TYPE_FIGHTER: id_ship_type = 0x412b; break; - case SHIP_TYPE_BOMBER: id_ship_type = 0xb7ce; break; - case SHIP_TYPE_FREIGHTER: id_ship_type = 0xb617; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x17a3a1a985f75aecull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->flags ) ); } - (void) id_ship_type; - bytes += 3 + 2; /* ship_type: the variant's name hash */ } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* object_id */ + if ( !( value->object_id == 0 ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0bdab42c07f19812ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->object_id ) ); } - (void) id_team; - bytes += 3 + 2; /* team: the variant's name hash */ } - if ( value->has_target_lock != 0 ) { bytes += 3 + 1; } /* has_target_lock */ - if ( value->predicted_explode != 0 ) { bytes += 3 + 1; } /* predicted_explode */ - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_save_body( TableWriter * w, const RenderShip * value ) -{ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_position > 2 ) /* all-default nested elides */ + { /* target_object_id */ + if ( !( value->target_object_id == 0 ) ) { - table_writer_put16( w, 0xdd45 ); table_writer_put8( w, 13 ); /* position */ - table_writer_put32( w, (uint32_t) body_position ); - if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6a0c6a156952b9c2ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->target_object_id ) ); } } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_rotation > 2 ) /* all-default nested elides */ + { /* thrust */ + if ( !( value->thrust == 0.0f ) ) { - table_writer_put16( w, 0x60f3 ); table_writer_put8( w, 13 ); /* rotation */ - table_writer_put32( w, (uint32_t) body_rotation ); - if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xcfd587cb3100cd73ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->thrust ) ); } } - if ( value->flags != 0 ) - { - table_writer_put16( w, 0xe64b ); table_writer_put8( w, 9 ); /* flags */ - table_writer_put64( w, (uint64_t) ( value->flags ) ); + { /* object_sequence */ + if ( !( value->object_sequence == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5de0015285763c46ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->object_sequence ) ); + } } - if ( value->object_id != 0 ) - { - table_writer_put16( w, 0xdc71 ); table_writer_put8( w, 8 ); /* object_id */ - table_writer_put32( w, (uint32_t) ( value->object_id ) ); + { /* ship_type */ + if ( !( value->ship_type == SHIP_TYPE_NONE ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1f7d2e86a2e77268ull ); table_writer_put8( w, 30 ); + switch ( value->ship_type ) + { + case SHIP_TYPE_NONE: table_writer_leb( w, 0 ); break; + case SHIP_TYPE_FIGHTER: table_writer_id( w, 0xd011f7c3c15285c2ull ); break; + case SHIP_TYPE_BOMBER: table_writer_id( w, 0xa8216aa1c554cb8aull ); break; + case SHIP_TYPE_FREIGHTER: table_writer_id( w, 0x6c2321a3d00e23dbull ); break; + default: return 0; /* no declared identity */ + } + } } - if ( value->target_object_id != 0 ) - { - table_writer_put16( w, 0x38dc ); table_writer_put8( w, 8 ); /* target_object_id */ - table_writer_put32( w, (uint32_t) ( value->target_object_id ) ); - } - if ( value->thrust != 0.0f ) - { - table_writer_put16( w, 0x9e51 ); table_writer_put8( w, 10 ); /* thrust */ - table_writer_put32( w, table_float_to_bits( value->thrust ) ); - } - if ( value->object_sequence != 0 ) - { - table_writer_put16( w, 0x57ee ); table_writer_put8( w, 6 ); /* object_sequence */ - table_writer_put8( w, (uint8_t) ( value->object_sequence ) ); - } - if ( value->ship_type != SHIP_TYPE_NONE ) - { - uint16_t id_ship_type = 0; - switch ( value->ship_type ) /* ShipType rides as the u16 hash of its VARIANT NAME (§5) */ + { /* team */ + if ( !( value->team == TEAM_NONE ) ) { - case SHIP_TYPE_NONE: id_ship_type = 0; break; - case SHIP_TYPE_FIGHTER: id_ship_type = 0x412b; break; - case SHIP_TYPE_BOMBER: id_ship_type = 0xb7ce; break; - case SHIP_TYPE_FREIGHTER: id_ship_type = 0xb617; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfa23d9ef19afc32cull ); table_writer_put8( w, 30 ); + switch ( value->team ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0x38e9 ); table_writer_put8( w, 7 ); /* ship_type */ - table_writer_put16( w, id_ship_type ); } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* has_target_lock */ + if ( !( value->has_target_lock == 0 ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1554fa45a1220171ull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->has_target_lock ? 1 : 0 ); } - table_writer_put16( w, 0xdff1 ); table_writer_put8( w, 7 ); /* team */ - table_writer_put16( w, id_team ); - } - if ( value->has_target_lock != 0 ) - { - table_writer_put16( w, 0xf223 ); table_writer_put8( w, 1 ); /* has_target_lock */ - table_writer_put8( w, value->has_target_lock ? 1 : 0 ); } - if ( value->predicted_explode != 0 ) - { - table_writer_put16( w, 0x88bd ); table_writer_put8( w, 1 ); /* predicted_explode */ - table_writer_put8( w, value->predicted_explode ? 1 : 0 ); + { /* predicted_explode */ + if ( !( value->predicted_explode == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4d5be97ba1b9cb81ull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->predicted_explode ? 1 : 0 ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_ship_measure( const RenderShip * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_ship_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_ship_save( const RenderShip * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_ship_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_ship_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_load_body( TableReader * r, RenderShip * value ) +static SCHEMA_UNUSED int render_ship_load_body( TableReader * r, RenderShip * value ) { - render_ship_reset( value ); /* prefill declared defaults in place, then overlay */ + render_ship_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xdd45: /* position */ + case 0x4cbf3a26fca1d74aull: /* position */ { if ( kind != 13 ) { @@ -1324,18 +2577,13 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->position ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->position ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->position ); } break; } - case 0x60f3: /* rotation */ + case 0xb51afb05cd34709full: /* rotation */ { if ( kind != 13 ) { @@ -1343,63 +2591,234 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_quaternion_load_body( &sub, &value->rotation ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_quaternion_load_body( &sub, &value->rotation ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_quaternion_reset( &value->rotation ); } break; } - case 0xe64b: /* flags */ + case 0x17a3a1a985f75aecull: /* flags */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->flags = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xdc71: /* object_id */ + case 0x0bdab42c07f19812ull: /* object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x38dc: /* target_object_id */ + case 0x6a0c6a156952b9c2ull: /* target_object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->target_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->target_object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->target_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x9e51: /* thrust */ + case 0xcfd587cb3100cd73ull: /* thrust */ { if ( kind != 10 ) { @@ -1407,11 +2826,19 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->thrust = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->thrust = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x57ee: /* object_sequence */ + case 0x5de0015285763c46ull: /* object_sequence */ { if ( kind != 6 ) { @@ -1419,59 +2846,73 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->object_sequence = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->object_sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x38e9: /* ship_type */ + case 0x1f7d2e86a2e77268ull: /* ship_type */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->ship_type = SHIP_TYPE_NONE; } + else { - case 0: value->ship_type = SHIP_TYPE_NONE; break; - case 0x412b: value->ship_type = SHIP_TYPE_FIGHTER; break; - case 0xb7ce: value->ship_type = SHIP_TYPE_BOMBER; break; - case 0xb617: value->ship_type = SHIP_TYPE_FREIGHTER; break; - default: value->ship_type = SHIP_TYPE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0xd011f7c3c15285c2ull: value->ship_type = SHIP_TYPE_FIGHTER; break; + case 0xa8216aa1c554cb8aull: value->ship_type = SHIP_TYPE_BOMBER; break; + case 0x6c2321a3d00e23dbull: value->ship_type = SHIP_TYPE_FREIGHTER; break; + default: value->ship_type = SHIP_TYPE_NONE; r->report->unknown++; break; + } } } break; } - case 0xdff1: /* team */ + case 0xfa23d9ef19afc32cull: /* team */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->team = TEAM_NONE; } + else { - case 0: value->team = TEAM_NONE; break; - case 0xbb03: value->team = TEAM_RED; break; - case 0xf630: value->team = TEAM_BLUE; break; - case 0x6e0f: value->team = TEAM_GREEN; break; - case 0xda27: value->team = TEAM_GOLD; break; - default: value->team = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x9ff1de19feac1b7cull: value->team = TEAM_RED; break; + case 0xecf3d3a7c1693e2dull: value->team = TEAM_BLUE; break; + case 0xcf00d78fd5953f1cull: value->team = TEAM_GREEN; break; + case 0xc416c97e0d3218b3ull: value->team = TEAM_GOLD; break; + default: value->team = TEAM_NONE; r->report->unknown++; break; + } } } break; } - case 0xf223: /* has_target_lock */ + case 0x1554fa45a1220171ull: /* has_target_lock */ { if ( kind != 1 ) { @@ -1479,11 +2920,19 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->has_target_lock = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->has_target_lock = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x88bd: /* predicted_explode */ + case 0x4d5be97ba1b9cb81ull: /* predicted_explode */ { if ( kind != 1 ) { @@ -1491,150 +2940,558 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_ship_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->predicted_explode = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->predicted_explode = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_ship_load( RenderShip * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_ship_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_ship_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_turret_measure( const RenderTurret * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return -1; } - if ( body_rotation > 2 ) { bytes += 3 + 4 + body_rotation; } /* rotation: all-default nested elides */ - } - if ( value->flags != 0 ) { bytes += 3 + 8; } /* flags */ - if ( value->object_id != 0 ) { bytes += 3 + 4; } /* object_id */ - if ( value->parent_object_id != 0 ) { bytes += 3 + 4; } /* parent_object_id */ - if ( value->turret_index != 0 ) { bytes += 3 + 4; } /* turret_index */ - if ( value->target_object_id != 0 ) { bytes += 3 + 4; } /* target_object_id */ - if ( value->object_sequence != 0 ) { bytes += 3 + 1; } /* object_sequence */ - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ +static SCHEMA_UNUSED int render_ship_save_message_body(TableBitWriter * w,const RenderShip * value) +{ + (void)value; + { /* position */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->position)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->position)) return 0; + } + } + { /* rotation */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_quaternion_save_message_body(&probe,&value->rotation)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + if(!render_quaternion_save_message_body(w,&value->rotation)) return 0; + } + } + { /* flags */ + if(!(value->flags == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,50,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->flags)-UINT64_C(0),4); + } + } + { /* object_id */ + if(!(value->object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,25,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->object_id)-UINT64_C(0),32); + } + } + { /* target_object_id */ + if(!(value->target_object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,17,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->target_object_id)-UINT64_C(0),32); + } + } + { /* thrust */ + if(!(value->thrust == 0.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,51,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->thrust),32); + } + } + { /* object_sequence */ + if(!(value->object_sequence == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,26,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->object_sequence)-UINT64_C(0),8); + } + } + { /* ship_type */ + if(!(value->ship_type == SHIP_TYPE_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,52,kTableMessageRefBitsHere); + switch(value->ship_type) { + case SHIP_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case SHIP_TYPE_FIGHTER: table_bit_put(w,66,kTableMessageRefBitsHere); + break; + case SHIP_TYPE_BOMBER: table_bit_put(w,67,kTableMessageRefBitsHere); + break; + case SHIP_TYPE_FREIGHTER: table_bit_put(w,68,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* team */ + if(!(value->team == TEAM_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + switch(value->team) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* has_target_lock */ + if(!(value->has_target_lock == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,53,kTableMessageRefBitsHere); + table_bit_put(w,value->has_target_lock ? 1 : 0,1); + } + } + { /* predicted_explode */ + if(!(value->predicted_explode == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,54,kTableMessageRefBitsHere); + table_bit_put(w,value->predicted_explode ? 1 : 0,1); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_ship_measure_messages(const RenderShip * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x4cbf3a26fca1d74a): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->position))return 0; + break; + } + case UINT64_C(0xb51afb05cd34709f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_quaternion_load_message_body(r,&value->rotation))return 0; + break; + } + case UINT64_C(0x17a3a1a985f75aec): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->flags=(ShipFlags)decoded; + } + break; + } + case UINT64_C(0x0bdab42c07f19812): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x6a0c6a156952b9c2): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->target_object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xcfd587cb3100cd73): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->thrust=decoded; + } + break; + } + case UINT64_C(0x5de0015285763c46): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->object_sequence=(uint8_t)decoded; + } + break; + } + case UINT64_C(0x1f7d2e86a2e77268): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->ship_type=SHIP_TYPE_NONE; + else switch(named->id) { + case UINT64_C(0xd011f7c3c15285c2): value->ship_type=SHIP_TYPE_FIGHTER; + break; + case UINT64_C(0xa8216aa1c554cb8a): value->ship_type=SHIP_TYPE_BOMBER; + break; + case UINT64_C(0x6c2321a3d00e23db): value->ship_type=SHIP_TYPE_FREIGHTER; + break; + default:value->ship_type=SHIP_TYPE_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0xfa23d9ef19afc32c): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->team=TEAM_NONE; + else switch(named->id) { + case UINT64_C(0x9ff1de19feac1b7c): value->team=TEAM_RED; + break; + case UINT64_C(0xecf3d3a7c1693e2d): value->team=TEAM_BLUE; + break; + case UINT64_C(0xcf00d78fd5953f1c): value->team=TEAM_GREEN; + break; + case UINT64_C(0xc416c97e0d3218b3): value->team=TEAM_GOLD; + break; + default:value->team=TEAM_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0x1554fa45a1220171): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->has_target_lock=lo!=0; + } + break; + } + case UINT64_C(0x4d5be97ba1b9cb81): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->predicted_explode=lo!=0; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_ship_load_messages(RenderShip * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_turret_save_body( TableWriter * w, const RenderTurret * value ) +{ + (void) value; + { /* rotation */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_quaternion_save_body( &default_probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb51afb05cd34709full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_quaternion_save_body( &probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + } } - (void) id_team; - bytes += 3 + 2; /* team: the variant's name hash */ } - if ( value->has_target_lock != 0 ) { bytes += 3 + 1; } /* has_target_lock */ - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_turret_save_body( TableWriter * w, const RenderTurret * value ) -{ - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_rotation > 2 ) /* all-default nested elides */ + { /* flags */ + if ( !( value->flags == 0 ) ) { - table_writer_put16( w, 0x60f3 ); table_writer_put8( w, 13 ); /* rotation */ - table_writer_put32( w, (uint32_t) body_rotation ); - if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x17a3a1a985f75aecull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->flags ) ); } } - if ( value->flags != 0 ) - { - table_writer_put16( w, 0xe64b ); table_writer_put8( w, 9 ); /* flags */ - table_writer_put64( w, (uint64_t) ( value->flags ) ); - } - if ( value->object_id != 0 ) - { - table_writer_put16( w, 0xdc71 ); table_writer_put8( w, 8 ); /* object_id */ - table_writer_put32( w, (uint32_t) ( value->object_id ) ); + { /* object_id */ + if ( !( value->object_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0bdab42c07f19812ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->object_id ) ); + } } - if ( value->parent_object_id != 0 ) - { - table_writer_put16( w, 0xeeb6 ); table_writer_put8( w, 8 ); /* parent_object_id */ - table_writer_put32( w, (uint32_t) ( value->parent_object_id ) ); + { /* parent_object_id */ + if ( !( value->parent_object_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0bee7b3cb4046c93ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->parent_object_id ) ); + } } - if ( value->turret_index != 0 ) - { - table_writer_put16( w, 0xae10 ); table_writer_put8( w, 8 ); /* turret_index */ - table_writer_put32( w, (uint32_t) ( value->turret_index ) ); + { /* turret_index */ + if ( !( value->turret_index == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x88a11a6aed541f54ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->turret_index ) ); + } } - if ( value->target_object_id != 0 ) - { - table_writer_put16( w, 0x38dc ); table_writer_put8( w, 8 ); /* target_object_id */ - table_writer_put32( w, (uint32_t) ( value->target_object_id ) ); + { /* target_object_id */ + if ( !( value->target_object_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6a0c6a156952b9c2ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->target_object_id ) ); + } } - if ( value->object_sequence != 0 ) - { - table_writer_put16( w, 0x57ee ); table_writer_put8( w, 6 ); /* object_sequence */ - table_writer_put8( w, (uint8_t) ( value->object_sequence ) ); + { /* object_sequence */ + if ( !( value->object_sequence == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5de0015285763c46ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->object_sequence ) ); + } } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* team */ + if ( !( value->team == TEAM_NONE ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfa23d9ef19afc32cull ); table_writer_put8( w, 30 ); + switch ( value->team ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xdff1 ); table_writer_put8( w, 7 ); /* team */ - table_writer_put16( w, id_team ); } - if ( value->has_target_lock != 0 ) - { - table_writer_put16( w, 0xf223 ); table_writer_put8( w, 1 ); /* has_target_lock */ - table_writer_put8( w, value->has_target_lock ? 1 : 0 ); + { /* has_target_lock */ + if ( !( value->has_target_lock == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1554fa45a1220171ull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->has_target_lock ? 1 : 0 ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_turret_measure( const RenderTurret * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_turret_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_turret_save( const RenderTurret * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_turret_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_turret_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_turret_load_body( TableReader * r, RenderTurret * value ) +static SCHEMA_UNUSED int render_turret_load_body( TableReader * r, RenderTurret * value ) { - render_turret_reset( value ); /* prefill declared defaults in place, then overlay */ + render_turret_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x60f3: /* rotation */ + case 0xb51afb05cd34709full: /* rotation */ { if ( kind != 13 ) { @@ -1642,93 +3499,372 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_turret_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_quaternion_load_body( &sub, &value->rotation ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_quaternion_load_body( &sub, &value->rotation ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_quaternion_reset( &value->rotation ); } break; } - case 0xe64b: /* flags */ + case 0x17a3a1a985f75aecull: /* flags */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->flags = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xdc71: /* object_id */ + case 0x0bdab42c07f19812ull: /* object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xeeb6: /* parent_object_id */ + case 0x0bee7b3cb4046c93ull: /* parent_object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->parent_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->parent_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->parent_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->parent_object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->parent_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->parent_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->parent_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xae10: /* turret_index */ + case 0x88a11a6aed541f54ull: /* turret_index */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->turret_index = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->turret_index = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->turret_index = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->turret_index = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->turret_index = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->turret_index = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->turret_index = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x38dc: /* target_object_id */ + case 0x6a0c6a156952b9c2ull: /* target_object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->target_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->target_object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->target_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->target_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x57ee: /* object_sequence */ + case 0x5de0015285763c46ull: /* object_sequence */ { if ( kind != 6 ) { @@ -1736,37 +3872,48 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_turret_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->object_sequence = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->object_sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xdff1: /* team */ + case 0xfa23d9ef19afc32cull: /* team */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->team = TEAM_NONE; } + else { - case 0: value->team = TEAM_NONE; break; - case 0xbb03: value->team = TEAM_RED; break; - case 0xf630: value->team = TEAM_BLUE; break; - case 0x6e0f: value->team = TEAM_GREEN; break; - case 0xda27: value->team = TEAM_GOLD; break; - default: value->team = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x9ff1de19feac1b7cull: value->team = TEAM_RED; break; + case 0xecf3d3a7c1693e2dull: value->team = TEAM_BLUE; break; + case 0xcf00d78fd5953f1cull: value->team = TEAM_GREEN; break; + case 0xc416c97e0d3218b3ull: value->team = TEAM_GOLD; break; + default: value->team = TEAM_NONE; r->report->unknown++; break; + } } } break; } - case 0xf223: /* has_target_lock */ + case 0x1554fa45a1220171ull: /* has_target_lock */ { if ( kind != 1 ) { @@ -1774,167 +3921,502 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_turret_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->has_target_lock = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->has_target_lock = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_turret_load( RenderTurret * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_turret_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_turret_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_missile_measure( const RenderMissile * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return -1; } - if ( body_position > 2 ) { bytes += 3 + 4 + body_position; } /* position: all-default nested elides */ - } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return -1; } - if ( body_rotation > 2 ) { bytes += 3 + 4 + body_rotation; } /* rotation: all-default nested elides */ - } - if ( value->flags != 0 ) { bytes += 3 + 8; } /* flags */ - if ( value->object_id != 0 ) { bytes += 3 + 4; } /* object_id */ - if ( value->object_sequence != 0 ) { bytes += 3 + 1; } /* object_sequence */ - if ( value->missile_type != MISSILE_TYPE_NONE ) - { - uint16_t id_missile_type = 0; - switch ( value->missile_type ) /* MissileType rides as the u16 hash of its VARIANT NAME (§5) */ +static SCHEMA_UNUSED int render_turret_save_message_body(TableBitWriter * w,const RenderTurret * value) +{ + (void)value; + { /* rotation */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_quaternion_save_message_body(&probe,&value->rotation)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + if(!render_quaternion_save_message_body(w,&value->rotation)) return 0; + } + } + { /* flags */ + if(!(value->flags == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,20,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->flags)-UINT64_C(0),64); + } + } + { /* object_id */ + if(!(value->object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,25,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->object_id)-UINT64_C(0),32); + } + } + { /* parent_object_id */ + if(!(value->parent_object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,29,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->parent_object_id)-UINT64_C(0),32); + } + } + { /* turret_index */ + if(!(value->turret_index == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,56,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->turret_index)-UINT64_C(0),32); + } + } + { /* target_object_id */ + if(!(value->target_object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,17,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->target_object_id)-UINT64_C(0),32); + } + } + { /* object_sequence */ + if(!(value->object_sequence == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,26,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->object_sequence)-UINT64_C(0),8); + } + } + { /* team */ + if(!(value->team == TEAM_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + switch(value->team) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* has_target_lock */ + if(!(value->has_target_lock == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,53,kTableMessageRefBitsHere); + table_bit_put(w,value->has_target_lock ? 1 : 0,1); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_turret_measure_messages(const RenderTurret * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xb51afb05cd34709f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_quaternion_load_message_body(r,&value->rotation))return 0; + break; + } + case UINT64_C(0x17a3a1a985f75aec): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->flags=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x0bdab42c07f19812): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x0bee7b3cb4046c93): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->parent_object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x88a11a6aed541f54): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->turret_index=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x6a0c6a156952b9c2): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->target_object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x5de0015285763c46): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->object_sequence=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xfa23d9ef19afc32c): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->team=TEAM_NONE; + else switch(named->id) { + case UINT64_C(0x9ff1de19feac1b7c): value->team=TEAM_RED; + break; + case UINT64_C(0xecf3d3a7c1693e2d): value->team=TEAM_BLUE; + break; + case UINT64_C(0xcf00d78fd5953f1c): value->team=TEAM_GREEN; + break; + case UINT64_C(0xc416c97e0d3218b3): value->team=TEAM_GOLD; + break; + default:value->team=TEAM_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0x1554fa45a1220171): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->has_target_lock=lo!=0; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_turret_load_messages(RenderTurret * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_missile_save_body( TableWriter * w, const RenderMissile * value ) +{ + (void) value; + { /* position */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->position ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case MISSILE_TYPE_NONE: id_missile_type = 0; break; - case MISSILE_TYPE_SEEKER: id_missile_type = 0xf8ff; break; - case MISSILE_TYPE_DUMB: id_missile_type = 0xd533; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4cbf3a26fca1d74aull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->position ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + } } - (void) id_missile_type; - bytes += 3 + 2; /* missile_type: the variant's name hash */ } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* rotation */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_quaternion_save_body( &default_probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb51afb05cd34709full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_quaternion_save_body( &probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + } } - (void) id_team; - bytes += 3 + 2; /* team: the variant's name hash */ } - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_missile_save_body( TableWriter * w, const RenderMissile * value ) -{ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_position > 2 ) /* all-default nested elides */ + { /* flags */ + if ( !( value->flags == 0 ) ) { - table_writer_put16( w, 0xdd45 ); table_writer_put8( w, 13 ); /* position */ - table_writer_put32( w, (uint32_t) body_position ); - if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x17a3a1a985f75aecull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->flags ) ); } } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_rotation > 2 ) /* all-default nested elides */ + { /* object_id */ + if ( !( value->object_id == 0 ) ) { - table_writer_put16( w, 0x60f3 ); table_writer_put8( w, 13 ); /* rotation */ - table_writer_put32( w, (uint32_t) body_rotation ); - if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0bdab42c07f19812ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->object_id ) ); } } - if ( value->flags != 0 ) - { - table_writer_put16( w, 0xe64b ); table_writer_put8( w, 9 ); /* flags */ - table_writer_put64( w, (uint64_t) ( value->flags ) ); - } - if ( value->object_id != 0 ) - { - table_writer_put16( w, 0xdc71 ); table_writer_put8( w, 8 ); /* object_id */ - table_writer_put32( w, (uint32_t) ( value->object_id ) ); - } - if ( value->object_sequence != 0 ) - { - table_writer_put16( w, 0x57ee ); table_writer_put8( w, 6 ); /* object_sequence */ - table_writer_put8( w, (uint8_t) ( value->object_sequence ) ); + { /* object_sequence */ + if ( !( value->object_sequence == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5de0015285763c46ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->object_sequence ) ); + } } - if ( value->missile_type != MISSILE_TYPE_NONE ) - { - uint16_t id_missile_type = 0; - switch ( value->missile_type ) /* MissileType rides as the u16 hash of its VARIANT NAME (§5) */ + { /* missile_type */ + if ( !( value->missile_type == MISSILE_TYPE_NONE ) ) { - case MISSILE_TYPE_NONE: id_missile_type = 0; break; - case MISSILE_TYPE_SEEKER: id_missile_type = 0xf8ff; break; - case MISSILE_TYPE_DUMB: id_missile_type = 0xd533; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x151b2a0e2c07b4bcull ); table_writer_put8( w, 30 ); + switch ( value->missile_type ) + { + case MISSILE_TYPE_NONE: table_writer_leb( w, 0 ); break; + case MISSILE_TYPE_SEEKER: table_writer_id( w, 0xf5d0a4d25a76afcaull ); break; + case MISSILE_TYPE_DUMB: table_writer_id( w, 0x478d7972f6d9075dull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0x423a ); table_writer_put8( w, 7 ); /* missile_type */ - table_writer_put16( w, id_missile_type ); } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* team */ + if ( !( value->team == TEAM_NONE ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfa23d9ef19afc32cull ); table_writer_put8( w, 30 ); + switch ( value->team ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xdff1 ); table_writer_put8( w, 7 ); /* team */ - table_writer_put16( w, id_team ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_missile_measure( const RenderMissile * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_missile_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_missile_save( const RenderMissile * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_missile_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_missile_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_missile_load_body( TableReader * r, RenderMissile * value ) +static SCHEMA_UNUSED int render_missile_load_body( TableReader * r, RenderMissile * value ) { - render_missile_reset( value ); /* prefill declared defaults in place, then overlay */ + render_missile_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xdd45: /* position */ + case 0x4cbf3a26fca1d74aull: /* position */ { if ( kind != 13 ) { @@ -1942,18 +4424,13 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_missile_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->position ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->position ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->position ); } break; } - case 0x60f3: /* rotation */ + case 0xb51afb05cd34709full: /* rotation */ { if ( kind != 13 ) { @@ -1961,48 +4438,165 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_missile_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_quaternion_load_body( &sub, &value->rotation ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_quaternion_load_body( &sub, &value->rotation ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_quaternion_reset( &value->rotation ); } break; } - case 0xe64b: /* flags */ + case 0x17a3a1a985f75aecull: /* flags */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->flags = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xdc71: /* object_id */ + case 0x0bdab42c07f19812ull: /* object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x57ee: /* object_sequence */ + case 0x5de0015285763c46ull: /* object_sequence */ { if ( kind != 6 ) { @@ -2010,216 +4604,518 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_missile_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->object_sequence = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->object_sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x423a: /* missile_type */ + case 0x151b2a0e2c07b4bcull: /* missile_type */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->missile_type = MISSILE_TYPE_NONE; } + else { - case 0: value->missile_type = MISSILE_TYPE_NONE; break; - case 0xf8ff: value->missile_type = MISSILE_TYPE_SEEKER; break; - case 0xd533: value->missile_type = MISSILE_TYPE_DUMB; break; - default: value->missile_type = MISSILE_TYPE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0xf5d0a4d25a76afcaull: value->missile_type = MISSILE_TYPE_SEEKER; break; + case 0x478d7972f6d9075dull: value->missile_type = MISSILE_TYPE_DUMB; break; + default: value->missile_type = MISSILE_TYPE_NONE; r->report->unknown++; break; + } } } break; } - case 0xdff1: /* team */ + case 0xfa23d9ef19afc32cull: /* team */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->team = TEAM_NONE; } + else { - case 0: value->team = TEAM_NONE; break; - case 0xbb03: value->team = TEAM_RED; break; - case 0xf630: value->team = TEAM_BLUE; break; - case 0x6e0f: value->team = TEAM_GREEN; break; - case 0xda27: value->team = TEAM_GOLD; break; - default: value->team = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x9ff1de19feac1b7cull: value->team = TEAM_RED; break; + case 0xecf3d3a7c1693e2dull: value->team = TEAM_BLUE; break; + case 0xcf00d78fd5953f1cull: value->team = TEAM_GREEN; break; + case 0xc416c97e0d3218b3ull: value->team = TEAM_GOLD; break; + default: value->team = TEAM_NONE; r->report->unknown++; break; + } } } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_missile_load( RenderMissile * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_missile_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_missile_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_dynamic_prop_measure( const RenderDynamicProp * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return -1; } - if ( body_position > 2 ) { bytes += 3 + 4 + body_position; } /* position: all-default nested elides */ - } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return -1; } - if ( body_rotation > 2 ) { bytes += 3 + 4 + body_rotation; } /* rotation: all-default nested elides */ - } - if ( value->flags != 0 ) { bytes += 3 + 8; } /* flags */ - if ( value->object_id != 0 ) { bytes += 3 + 4; } /* object_id */ - if ( value->object_sequence != 0 ) { bytes += 3 + 1; } /* object_sequence */ - if ( value->prop_type != PROP_TYPE_NONE ) - { - uint16_t id_prop_type = 0; - switch ( value->prop_type ) /* PropType rides as the u16 hash of its VARIANT NAME (§5) */ +static SCHEMA_UNUSED int render_missile_save_message_body(TableBitWriter * w,const RenderMissile * value) +{ + (void)value; + { /* position */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->position)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->position)) return 0; + } + } + { /* rotation */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_quaternion_save_message_body(&probe,&value->rotation)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + if(!render_quaternion_save_message_body(w,&value->rotation)) return 0; + } + } + { /* flags */ + if(!(value->flags == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,20,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->flags)-UINT64_C(0),64); + } + } + { /* object_id */ + if(!(value->object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,25,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->object_id)-UINT64_C(0),32); + } + } + { /* object_sequence */ + if(!(value->object_sequence == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,26,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->object_sequence)-UINT64_C(0),8); + } + } + { /* missile_type */ + if(!(value->missile_type == MISSILE_TYPE_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,45,kTableMessageRefBitsHere); + switch(value->missile_type) { + case MISSILE_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case MISSILE_TYPE_SEEKER: table_bit_put(w,61,kTableMessageRefBitsHere); + break; + case MISSILE_TYPE_DUMB: table_bit_put(w,62,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* team */ + if(!(value->team == TEAM_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + switch(value->team) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_missile_measure_messages(const RenderMissile * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x4cbf3a26fca1d74a): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->position))return 0; + break; + } + case UINT64_C(0xb51afb05cd34709f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_quaternion_load_message_body(r,&value->rotation))return 0; + break; + } + case UINT64_C(0x17a3a1a985f75aec): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->flags=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x0bdab42c07f19812): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x5de0015285763c46): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->object_sequence=(uint8_t)decoded; + } + break; + } + case UINT64_C(0x151b2a0e2c07b4bc): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->missile_type=MISSILE_TYPE_NONE; + else switch(named->id) { + case UINT64_C(0xf5d0a4d25a76afca): value->missile_type=MISSILE_TYPE_SEEKER; + break; + case UINT64_C(0x478d7972f6d9075d): value->missile_type=MISSILE_TYPE_DUMB; + break; + default:value->missile_type=MISSILE_TYPE_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0xfa23d9ef19afc32c): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->team=TEAM_NONE; + else switch(named->id) { + case UINT64_C(0x9ff1de19feac1b7c): value->team=TEAM_RED; + break; + case UINT64_C(0xecf3d3a7c1693e2d): value->team=TEAM_BLUE; + break; + case UINT64_C(0xcf00d78fd5953f1c): value->team=TEAM_GREEN; + break; + case UINT64_C(0xc416c97e0d3218b3): value->team=TEAM_GOLD; + break; + default:value->team=TEAM_NONE; + r->report->unknown++; + break; + } } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_missile_load_messages(RenderMissile * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_dynamic_prop_save_body( TableWriter * w, const RenderDynamicProp * value ) +{ + (void) value; + { /* position */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->position ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case PROP_TYPE_NONE: id_prop_type = 0; break; - case PROP_TYPE_ROCK: id_prop_type = 0x2c93; break; - case PROP_TYPE_STATION: id_prop_type = 0x4532; break; - case PROP_TYPE_BEACON: id_prop_type = 0x4b61; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4cbf3a26fca1d74aull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->position ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + } } - (void) id_prop_type; - bytes += 3 + 2; /* prop_type: the variant's name hash */ } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* rotation */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_quaternion_save_body( &default_probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb51afb05cd34709full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_quaternion_save_body( &probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + } } - (void) id_team; - bytes += 3 + 2; /* team: the variant's name hash */ } - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_dynamic_prop_save_body( TableWriter * w, const RenderDynamicProp * value ) -{ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_position > 2 ) /* all-default nested elides */ + { /* flags */ + if ( !( value->flags == 0 ) ) { - table_writer_put16( w, 0xdd45 ); table_writer_put8( w, 13 ); /* position */ - table_writer_put32( w, (uint32_t) body_position ); - if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x17a3a1a985f75aecull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->flags ) ); } } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_rotation > 2 ) /* all-default nested elides */ + { /* object_id */ + if ( !( value->object_id == 0 ) ) { - table_writer_put16( w, 0x60f3 ); table_writer_put8( w, 13 ); /* rotation */ - table_writer_put32( w, (uint32_t) body_rotation ); - if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0bdab42c07f19812ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->object_id ) ); } } - if ( value->flags != 0 ) - { - table_writer_put16( w, 0xe64b ); table_writer_put8( w, 9 ); /* flags */ - table_writer_put64( w, (uint64_t) ( value->flags ) ); - } - if ( value->object_id != 0 ) - { - table_writer_put16( w, 0xdc71 ); table_writer_put8( w, 8 ); /* object_id */ - table_writer_put32( w, (uint32_t) ( value->object_id ) ); - } - if ( value->object_sequence != 0 ) - { - table_writer_put16( w, 0x57ee ); table_writer_put8( w, 6 ); /* object_sequence */ - table_writer_put8( w, (uint8_t) ( value->object_sequence ) ); + { /* object_sequence */ + if ( !( value->object_sequence == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5de0015285763c46ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->object_sequence ) ); + } } - if ( value->prop_type != PROP_TYPE_NONE ) - { - uint16_t id_prop_type = 0; - switch ( value->prop_type ) /* PropType rides as the u16 hash of its VARIANT NAME (§5) */ + { /* prop_type */ + if ( !( value->prop_type == PROP_TYPE_NONE ) ) { - case PROP_TYPE_NONE: id_prop_type = 0; break; - case PROP_TYPE_ROCK: id_prop_type = 0x2c93; break; - case PROP_TYPE_STATION: id_prop_type = 0x4532; break; - case PROP_TYPE_BEACON: id_prop_type = 0x4b61; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xe5626f155e4c5dadull ); table_writer_put8( w, 30 ); + switch ( value->prop_type ) + { + case PROP_TYPE_NONE: table_writer_leb( w, 0 ); break; + case PROP_TYPE_ROCK: table_writer_id( w, 0xcaa6172bef74e234ull ); break; + case PROP_TYPE_STATION: table_writer_id( w, 0x4f98acac2852d653ull ); break; + case PROP_TYPE_BEACON: table_writer_id( w, 0x7f3a7e4744ea3019ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xe338 ); table_writer_put8( w, 7 ); /* prop_type */ - table_writer_put16( w, id_prop_type ); } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* team */ + if ( !( value->team == TEAM_NONE ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfa23d9ef19afc32cull ); table_writer_put8( w, 30 ); + switch ( value->team ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xdff1 ); table_writer_put8( w, 7 ); /* team */ - table_writer_put16( w, id_team ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_dynamic_prop_measure( const RenderDynamicProp * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_dynamic_prop_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_dynamic_prop_save( const RenderDynamicProp * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_dynamic_prop_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_dynamic_prop_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_dynamic_prop_load_body( TableReader * r, RenderDynamicProp * value ) +static SCHEMA_UNUSED int render_dynamic_prop_load_body( TableReader * r, RenderDynamicProp * value ) { - render_dynamic_prop_reset( value ); /* prefill declared defaults in place, then overlay */ + render_dynamic_prop_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xdd45: /* position */ + case 0x4cbf3a26fca1d74aull: /* position */ { if ( kind != 13 ) { @@ -2227,18 +5123,13 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_dynamic_prop_load_ if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->position ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->position ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->position ); } break; } - case 0x60f3: /* rotation */ + case 0xb51afb05cd34709full: /* rotation */ { if ( kind != 13 ) { @@ -2246,48 +5137,165 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_dynamic_prop_load_ if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_quaternion_load_body( &sub, &value->rotation ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_quaternion_load_body( &sub, &value->rotation ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_quaternion_reset( &value->rotation ); } break; } - case 0xe64b: /* flags */ + case 0x17a3a1a985f75aecull: /* flags */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->flags = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xdc71: /* object_id */ + case 0x0bdab42c07f19812ull: /* object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x57ee: /* object_sequence */ + case 0x5de0015285763c46ull: /* object_sequence */ { if ( kind != 6 ) { @@ -2295,217 +5303,523 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_dynamic_prop_load_ if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->object_sequence = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->object_sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe338: /* prop_type */ + case 0xe5626f155e4c5dadull: /* prop_type */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->prop_type = PROP_TYPE_NONE; } + else { - case 0: value->prop_type = PROP_TYPE_NONE; break; - case 0x2c93: value->prop_type = PROP_TYPE_ROCK; break; - case 0x4532: value->prop_type = PROP_TYPE_STATION; break; - case 0x4b61: value->prop_type = PROP_TYPE_BEACON; break; - default: value->prop_type = PROP_TYPE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0xcaa6172bef74e234ull: value->prop_type = PROP_TYPE_ROCK; break; + case 0x4f98acac2852d653ull: value->prop_type = PROP_TYPE_STATION; break; + case 0x7f3a7e4744ea3019ull: value->prop_type = PROP_TYPE_BEACON; break; + default: value->prop_type = PROP_TYPE_NONE; r->report->unknown++; break; + } } } break; } - case 0xdff1: /* team */ + case 0xfa23d9ef19afc32cull: /* team */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->team = TEAM_NONE; } + else { - case 0: value->team = TEAM_NONE; break; - case 0xbb03: value->team = TEAM_RED; break; - case 0xf630: value->team = TEAM_BLUE; break; - case 0x6e0f: value->team = TEAM_GREEN; break; - case 0xda27: value->team = TEAM_GOLD; break; - default: value->team = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x9ff1de19feac1b7cull: value->team = TEAM_RED; break; + case 0xecf3d3a7c1693e2dull: value->team = TEAM_BLUE; break; + case 0xcf00d78fd5953f1cull: value->team = TEAM_GREEN; break; + case 0xc416c97e0d3218b3ull: value->team = TEAM_GOLD; break; + default: value->team = TEAM_NONE; r->report->unknown++; break; + } } } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_dynamic_prop_load( RenderDynamicProp * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_dynamic_prop_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_dynamic_prop_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_static_prop_measure( const RenderStaticProp * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return -1; } - if ( body_position > 2 ) { bytes += 3 + 4 + body_position; } /* position: all-default nested elides */ - } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return -1; } - if ( body_rotation > 2 ) { bytes += 3 + 4 + body_rotation; } /* rotation: all-default nested elides */ - } - if ( value->scale != 0.0 ) { bytes += 3 + 8; } /* scale */ - if ( value->flags != 0 ) { bytes += 3 + 8; } /* flags */ - if ( value->static_prop_id != 0 ) { bytes += 3 + 4; } /* static_prop_id */ - if ( value->prop_type != PROP_TYPE_NONE ) - { - uint16_t id_prop_type = 0; - switch ( value->prop_type ) /* PropType rides as the u16 hash of its VARIANT NAME (§5) */ +static SCHEMA_UNUSED int render_dynamic_prop_save_message_body(TableBitWriter * w,const RenderDynamicProp * value) +{ + (void)value; + { /* position */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->position)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->position)) return 0; + } + } + { /* rotation */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_quaternion_save_message_body(&probe,&value->rotation)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + if(!render_quaternion_save_message_body(w,&value->rotation)) return 0; + } + } + { /* flags */ + if(!(value->flags == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,20,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->flags)-UINT64_C(0),64); + } + } + { /* object_id */ + if(!(value->object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,25,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->object_id)-UINT64_C(0),32); + } + } + { /* object_sequence */ + if(!(value->object_sequence == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,26,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->object_sequence)-UINT64_C(0),8); + } + } + { /* prop_type */ + if(!(value->prop_type == PROP_TYPE_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,23,kTableMessageRefBitsHere); + switch(value->prop_type) { + case PROP_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case PROP_TYPE_ROCK: table_bit_put(w,63,kTableMessageRefBitsHere); + break; + case PROP_TYPE_STATION: table_bit_put(w,64,kTableMessageRefBitsHere); + break; + case PROP_TYPE_BEACON: table_bit_put(w,65,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* team */ + if(!(value->team == TEAM_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + switch(value->team) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_dynamic_prop_measure_messages(const RenderDynamicProp * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x4cbf3a26fca1d74a): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->position))return 0; + break; + } + case UINT64_C(0xb51afb05cd34709f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_quaternion_load_message_body(r,&value->rotation))return 0; + break; + } + case UINT64_C(0x17a3a1a985f75aec): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->flags=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x0bdab42c07f19812): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x5de0015285763c46): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->object_sequence=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xe5626f155e4c5dad): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->prop_type=PROP_TYPE_NONE; + else switch(named->id) { + case UINT64_C(0xcaa6172bef74e234): value->prop_type=PROP_TYPE_ROCK; + break; + case UINT64_C(0x4f98acac2852d653): value->prop_type=PROP_TYPE_STATION; + break; + case UINT64_C(0x7f3a7e4744ea3019): value->prop_type=PROP_TYPE_BEACON; + break; + default:value->prop_type=PROP_TYPE_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0xfa23d9ef19afc32c): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->team=TEAM_NONE; + else switch(named->id) { + case UINT64_C(0x9ff1de19feac1b7c): value->team=TEAM_RED; + break; + case UINT64_C(0xecf3d3a7c1693e2d): value->team=TEAM_BLUE; + break; + case UINT64_C(0xcf00d78fd5953f1c): value->team=TEAM_GREEN; + break; + case UINT64_C(0xc416c97e0d3218b3): value->team=TEAM_GOLD; + break; + default:value->team=TEAM_NONE; + r->report->unknown++; + break; + } } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_dynamic_prop_load_messages(RenderDynamicProp * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_static_prop_save_body( TableWriter * w, const RenderStaticProp * value ) +{ + (void) value; + { /* position */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->position ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case PROP_TYPE_NONE: id_prop_type = 0; break; - case PROP_TYPE_ROCK: id_prop_type = 0x2c93; break; - case PROP_TYPE_STATION: id_prop_type = 0x4532; break; - case PROP_TYPE_BEACON: id_prop_type = 0x4b61; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4cbf3a26fca1d74aull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->position ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + } } - (void) id_prop_type; - bytes += 3 + 2; /* prop_type: the variant's name hash */ } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* rotation */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_quaternion_save_body( &default_probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb51afb05cd34709full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_quaternion_save_body( &probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + } } - (void) id_team; - bytes += 3 + 2; /* team: the variant's name hash */ } - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_static_prop_save_body( TableWriter * w, const RenderStaticProp * value ) -{ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_position > 2 ) /* all-default nested elides */ + { /* scale */ + if ( !( value->scale == 0.0 ) ) { - table_writer_put16( w, 0xdd45 ); table_writer_put8( w, 13 ); /* position */ - table_writer_put32( w, (uint32_t) body_position ); - if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6aacb9fbb71a1d91ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->scale ) ); } } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_rotation > 2 ) /* all-default nested elides */ + { /* flags */ + if ( !( value->flags == 0 ) ) { - table_writer_put16( w, 0x60f3 ); table_writer_put8( w, 13 ); /* rotation */ - table_writer_put32( w, (uint32_t) body_rotation ); - if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x17a3a1a985f75aecull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->flags ) ); } } - if ( value->scale != 0.0 ) - { - table_writer_put16( w, 0x9ee6 ); table_writer_put8( w, 11 ); /* scale */ - table_writer_put64( w, table_double_to_bits( value->scale ) ); - } - if ( value->flags != 0 ) - { - table_writer_put16( w, 0xe64b ); table_writer_put8( w, 9 ); /* flags */ - table_writer_put64( w, (uint64_t) ( value->flags ) ); - } - if ( value->static_prop_id != 0 ) - { - table_writer_put16( w, 0x0f74 ); table_writer_put8( w, 8 ); /* static_prop_id */ - table_writer_put32( w, (uint32_t) ( value->static_prop_id ) ); + { /* static_prop_id */ + if ( !( value->static_prop_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd23da7ab574b95c3ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->static_prop_id ) ); + } } - if ( value->prop_type != PROP_TYPE_NONE ) - { - uint16_t id_prop_type = 0; - switch ( value->prop_type ) /* PropType rides as the u16 hash of its VARIANT NAME (§5) */ + { /* prop_type */ + if ( !( value->prop_type == PROP_TYPE_NONE ) ) { - case PROP_TYPE_NONE: id_prop_type = 0; break; - case PROP_TYPE_ROCK: id_prop_type = 0x2c93; break; - case PROP_TYPE_STATION: id_prop_type = 0x4532; break; - case PROP_TYPE_BEACON: id_prop_type = 0x4b61; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xe5626f155e4c5dadull ); table_writer_put8( w, 30 ); + switch ( value->prop_type ) + { + case PROP_TYPE_NONE: table_writer_leb( w, 0 ); break; + case PROP_TYPE_ROCK: table_writer_id( w, 0xcaa6172bef74e234ull ); break; + case PROP_TYPE_STATION: table_writer_id( w, 0x4f98acac2852d653ull ); break; + case PROP_TYPE_BEACON: table_writer_id( w, 0x7f3a7e4744ea3019ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xe338 ); table_writer_put8( w, 7 ); /* prop_type */ - table_writer_put16( w, id_prop_type ); } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* team */ + if ( !( value->team == TEAM_NONE ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfa23d9ef19afc32cull ); table_writer_put8( w, 30 ); + switch ( value->team ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xdff1 ); table_writer_put8( w, 7 ); /* team */ - table_writer_put16( w, id_team ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_static_prop_measure( const RenderStaticProp * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_static_prop_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_static_prop_save( const RenderStaticProp * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_static_prop_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_static_prop_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_static_prop_load_body( TableReader * r, RenderStaticProp * value ) +static SCHEMA_UNUSED int render_static_prop_load_body( TableReader * r, RenderStaticProp * value ) { - render_static_prop_reset( value ); /* prefill declared defaults in place, then overlay */ + render_static_prop_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xdd45: /* position */ + case 0x4cbf3a26fca1d74aull: /* position */ { if ( kind != 13 ) { @@ -2513,18 +5827,13 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_static_prop_load_b if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->position ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->position ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->position ); } break; } - case 0x60f3: /* rotation */ + case 0xb51afb05cd34709full: /* rotation */ { if ( kind != 13 ) { @@ -2532,269 +5841,724 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_static_prop_load_b if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_quaternion_load_body( &sub, &value->rotation ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_quaternion_load_body( &sub, &value->rotation ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_quaternion_reset( &value->rotation ); } break; } - case 0x9ee6: /* scale */ + case 0x6aacb9fbb71a1d91ull: /* scale */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->scale = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->scale = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->scale = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->scale = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->scale = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xe64b: /* flags */ + case 0x17a3a1a985f75aecull: /* flags */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->flags = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x0f74: /* static_prop_id */ + case 0xd23da7ab574b95c3ull: /* static_prop_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->static_prop_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->static_prop_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->static_prop_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->static_prop_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->static_prop_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->static_prop_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->static_prop_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe338: /* prop_type */ + case 0xe5626f155e4c5dadull: /* prop_type */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->prop_type = PROP_TYPE_NONE; } + else { - case 0: value->prop_type = PROP_TYPE_NONE; break; - case 0x2c93: value->prop_type = PROP_TYPE_ROCK; break; - case 0x4532: value->prop_type = PROP_TYPE_STATION; break; - case 0x4b61: value->prop_type = PROP_TYPE_BEACON; break; - default: value->prop_type = PROP_TYPE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0xcaa6172bef74e234ull: value->prop_type = PROP_TYPE_ROCK; break; + case 0x4f98acac2852d653ull: value->prop_type = PROP_TYPE_STATION; break; + case 0x7f3a7e4744ea3019ull: value->prop_type = PROP_TYPE_BEACON; break; + default: value->prop_type = PROP_TYPE_NONE; r->report->unknown++; break; + } } } break; } - case 0xdff1: /* team */ + case 0xfa23d9ef19afc32cull: /* team */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->team = TEAM_NONE; } + else { - case 0: value->team = TEAM_NONE; break; - case 0xbb03: value->team = TEAM_RED; break; - case 0xf630: value->team = TEAM_BLUE; break; - case 0x6e0f: value->team = TEAM_GREEN; break; - case 0xda27: value->team = TEAM_GOLD; break; - default: value->team = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x9ff1de19feac1b7cull: value->team = TEAM_RED; break; + case 0xecf3d3a7c1693e2dull: value->team = TEAM_BLUE; break; + case 0xcf00d78fd5953f1cull: value->team = TEAM_GREEN; break; + case 0xc416c97e0d3218b3ull: value->team = TEAM_GOLD; break; + default: value->team = TEAM_NONE; r->report->unknown++; break; + } } } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_static_prop_load( RenderStaticProp * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_static_prop_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_static_prop_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_cosmetic_prop_measure( const RenderCosmeticProp * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return -1; } - if ( body_position > 2 ) { bytes += 3 + 4 + body_position; } /* position: all-default nested elides */ - } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return -1; } - if ( body_rotation > 2 ) { bytes += 3 + 4 + body_rotation; } /* rotation: all-default nested elides */ - } - if ( value->scale != 0.0 ) { bytes += 3 + 8; } /* scale */ - if ( value->flags != 0 ) { bytes += 3 + 8; } /* flags */ - if ( value->cosmetic_prop_id != 0 ) { bytes += 3 + 4; } /* cosmetic_prop_id */ - if ( value->prop_sequence != 0 ) { bytes += 3 + 1; } /* prop_sequence */ - if ( value->prop_type != PROP_TYPE_NONE ) - { - uint16_t id_prop_type = 0; - switch ( value->prop_type ) /* PropType rides as the u16 hash of its VARIANT NAME (§5) */ +static SCHEMA_UNUSED int render_static_prop_save_message_body(TableBitWriter * w,const RenderStaticProp * value) +{ + (void)value; + { /* position */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->position)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->position)) return 0; + } + } + { /* rotation */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_quaternion_save_message_body(&probe,&value->rotation)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + if(!render_quaternion_save_message_body(w,&value->rotation)) return 0; + } + } + { /* scale */ + if(!(value->scale == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,19,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->scale),64); + } + } + { /* flags */ + if(!(value->flags == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,20,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->flags)-UINT64_C(0),64); + } + } + { /* static_prop_id */ + if(!(value->static_prop_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,55,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->static_prop_id)-UINT64_C(0),32); + } + } + { /* prop_type */ + if(!(value->prop_type == PROP_TYPE_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,23,kTableMessageRefBitsHere); + switch(value->prop_type) { + case PROP_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case PROP_TYPE_ROCK: table_bit_put(w,63,kTableMessageRefBitsHere); + break; + case PROP_TYPE_STATION: table_bit_put(w,64,kTableMessageRefBitsHere); + break; + case PROP_TYPE_BEACON: table_bit_put(w,65,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* team */ + if(!(value->team == TEAM_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + switch(value->team) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_static_prop_measure_messages(const RenderStaticProp * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x4cbf3a26fca1d74a): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->position))return 0; + break; + } + case UINT64_C(0xb51afb05cd34709f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_quaternion_load_message_body(r,&value->rotation))return 0; + break; + } + case UINT64_C(0x6aacb9fbb71a1d91): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->scale=decoded; + } + break; + } + case UINT64_C(0x17a3a1a985f75aec): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->flags=(uint64_t)decoded; + } + break; + } + case UINT64_C(0xd23da7ab574b95c3): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->static_prop_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xe5626f155e4c5dad): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->prop_type=PROP_TYPE_NONE; + else switch(named->id) { + case UINT64_C(0xcaa6172bef74e234): value->prop_type=PROP_TYPE_ROCK; + break; + case UINT64_C(0x4f98acac2852d653): value->prop_type=PROP_TYPE_STATION; + break; + case UINT64_C(0x7f3a7e4744ea3019): value->prop_type=PROP_TYPE_BEACON; + break; + default:value->prop_type=PROP_TYPE_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0xfa23d9ef19afc32c): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->team=TEAM_NONE; + else switch(named->id) { + case UINT64_C(0x9ff1de19feac1b7c): value->team=TEAM_RED; + break; + case UINT64_C(0xecf3d3a7c1693e2d): value->team=TEAM_BLUE; + break; + case UINT64_C(0xcf00d78fd5953f1c): value->team=TEAM_GREEN; + break; + case UINT64_C(0xc416c97e0d3218b3): value->team=TEAM_GOLD; + break; + default:value->team=TEAM_NONE; + r->report->unknown++; + break; + } } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_static_prop_load_messages(RenderStaticProp * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_cosmetic_prop_save_body( TableWriter * w, const RenderCosmeticProp * value ) +{ + (void) value; + { /* position */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->position ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case PROP_TYPE_NONE: id_prop_type = 0; break; - case PROP_TYPE_ROCK: id_prop_type = 0x2c93; break; - case PROP_TYPE_STATION: id_prop_type = 0x4532; break; - case PROP_TYPE_BEACON: id_prop_type = 0x4b61; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4cbf3a26fca1d74aull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->position ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + } } - (void) id_prop_type; - bytes += 3 + 2; /* prop_type: the variant's name hash */ } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* rotation */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_quaternion_save_body( &default_probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb51afb05cd34709full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_quaternion_save_body( &probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + } } - (void) id_team; - bytes += 3 + 2; /* team: the variant's name hash */ } - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_cosmetic_prop_save_body( TableWriter * w, const RenderCosmeticProp * value ) -{ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_position > 2 ) /* all-default nested elides */ + { /* scale */ + if ( !( value->scale == 0.0 ) ) { - table_writer_put16( w, 0xdd45 ); table_writer_put8( w, 13 ); /* position */ - table_writer_put32( w, (uint32_t) body_position ); - if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6aacb9fbb71a1d91ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->scale ) ); } } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_rotation > 2 ) /* all-default nested elides */ + { /* flags */ + if ( !( value->flags == 0 ) ) { - table_writer_put16( w, 0x60f3 ); table_writer_put8( w, 13 ); /* rotation */ - table_writer_put32( w, (uint32_t) body_rotation ); - if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x17a3a1a985f75aecull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->flags ) ); } } - if ( value->scale != 0.0 ) - { - table_writer_put16( w, 0x9ee6 ); table_writer_put8( w, 11 ); /* scale */ - table_writer_put64( w, table_double_to_bits( value->scale ) ); - } - if ( value->flags != 0 ) - { - table_writer_put16( w, 0xe64b ); table_writer_put8( w, 9 ); /* flags */ - table_writer_put64( w, (uint64_t) ( value->flags ) ); - } - if ( value->cosmetic_prop_id != 0 ) - { - table_writer_put16( w, 0x0843 ); table_writer_put8( w, 8 ); /* cosmetic_prop_id */ - table_writer_put32( w, (uint32_t) ( value->cosmetic_prop_id ) ); - } - if ( value->prop_sequence != 0 ) - { - table_writer_put16( w, 0x3069 ); table_writer_put8( w, 6 ); /* prop_sequence */ - table_writer_put8( w, (uint8_t) ( value->prop_sequence ) ); - } - if ( value->prop_type != PROP_TYPE_NONE ) - { - uint16_t id_prop_type = 0; - switch ( value->prop_type ) /* PropType rides as the u16 hash of its VARIANT NAME (§5) */ + { /* cosmetic_prop_id */ + if ( !( value->cosmetic_prop_id == 0 ) ) { - case PROP_TYPE_NONE: id_prop_type = 0; break; - case PROP_TYPE_ROCK: id_prop_type = 0x2c93; break; - case PROP_TYPE_STATION: id_prop_type = 0x4532; break; - case PROP_TYPE_BEACON: id_prop_type = 0x4b61; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb66e4449e56b2e26ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->cosmetic_prop_id ) ); } - table_writer_put16( w, 0xe338 ); table_writer_put8( w, 7 ); /* prop_type */ - table_writer_put16( w, id_prop_type ); } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* prop_sequence */ + if ( !( value->prop_sequence == 0 ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4d7bf73bcbddc228ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->prop_sequence ) ); } - table_writer_put16( w, 0xdff1 ); table_writer_put8( w, 7 ); /* team */ - table_writer_put16( w, id_team ); } - table_writer_put16( w, 0 ); /* terminator */ - return !w->overflow; -} + { /* prop_type */ + if ( !( value->prop_type == PROP_TYPE_NONE ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xe5626f155e4c5dadull ); table_writer_put8( w, 30 ); + switch ( value->prop_type ) + { + case PROP_TYPE_NONE: table_writer_leb( w, 0 ); break; + case PROP_TYPE_ROCK: table_writer_id( w, 0xcaa6172bef74e234ull ); break; + case PROP_TYPE_STATION: table_writer_id( w, 0x4f98acac2852d653ull ); break; + case PROP_TYPE_BEACON: table_writer_id( w, 0x7f3a7e4744ea3019ull ); break; + default: return 0; /* no declared identity */ + } + } + } + { /* team */ + if ( !( value->team == TEAM_NONE ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfa23d9ef19afc32cull ); table_writer_put8( w, 30 ); + switch ( value->team ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } + } + } + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int64_t render_cosmetic_prop_measure( const RenderCosmeticProp * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_cosmetic_prop_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} static SCHEMA_UNUSED int64_t render_cosmetic_prop_save( const RenderCosmeticProp * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_cosmetic_prop_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_cosmetic_prop_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_cosmetic_prop_load_body( TableReader * r, RenderCosmeticProp * value ) +static SCHEMA_UNUSED int render_cosmetic_prop_load_body( TableReader * r, RenderCosmeticProp * value ) { - render_cosmetic_prop_reset( value ); /* prefill declared defaults in place, then overlay */ + render_cosmetic_prop_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xdd45: /* position */ + case 0x4cbf3a26fca1d74aull: /* position */ { if ( kind != 13 ) { @@ -2802,18 +6566,13 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_cosmetic_prop_load if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->position ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->position ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->position ); } break; } - case 0x60f3: /* rotation */ + case 0xb51afb05cd34709full: /* rotation */ { if ( kind != 13 ) { @@ -2821,60 +6580,214 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_cosmetic_prop_load if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_quaternion_load_body( &sub, &value->rotation ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_quaternion_load_body( &sub, &value->rotation ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_quaternion_reset( &value->rotation ); } break; } - case 0x9ee6: /* scale */ + case 0x6aacb9fbb71a1d91ull: /* scale */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->scale = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->scale = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->scale = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->scale = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->scale = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xe64b: /* flags */ + case 0x17a3a1a985f75aecull: /* flags */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->flags = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->flags = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->flags = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->flags = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->flags = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x0843: /* cosmetic_prop_id */ + case 0xb66e4449e56b2e26ull: /* cosmetic_prop_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->cosmetic_prop_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->cosmetic_prop_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->cosmetic_prop_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->cosmetic_prop_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->cosmetic_prop_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->cosmetic_prop_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->cosmetic_prop_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x3069: /* prop_sequence */ + case 0x4d7bf73bcbddc228ull: /* prop_sequence */ { if ( kind != 6 ) { @@ -2882,209 +6795,541 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_cosmetic_prop_load if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->prop_sequence = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->prop_sequence = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe338: /* prop_type */ + case 0xe5626f155e4c5dadull: /* prop_type */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->prop_type = PROP_TYPE_NONE; } + else { - case 0: value->prop_type = PROP_TYPE_NONE; break; - case 0x2c93: value->prop_type = PROP_TYPE_ROCK; break; - case 0x4532: value->prop_type = PROP_TYPE_STATION; break; - case 0x4b61: value->prop_type = PROP_TYPE_BEACON; break; - default: value->prop_type = PROP_TYPE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0xcaa6172bef74e234ull: value->prop_type = PROP_TYPE_ROCK; break; + case 0x4f98acac2852d653ull: value->prop_type = PROP_TYPE_STATION; break; + case 0x7f3a7e4744ea3019ull: value->prop_type = PROP_TYPE_BEACON; break; + default: value->prop_type = PROP_TYPE_NONE; r->report->unknown++; break; + } } } break; } - case 0xdff1: /* team */ + case 0xfa23d9ef19afc32cull: /* team */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->team = TEAM_NONE; } + else { - case 0: value->team = TEAM_NONE; break; - case 0xbb03: value->team = TEAM_RED; break; - case 0xf630: value->team = TEAM_BLUE; break; - case 0x6e0f: value->team = TEAM_GREEN; break; - case 0xda27: value->team = TEAM_GOLD; break; - default: value->team = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x9ff1de19feac1b7cull: value->team = TEAM_RED; break; + case 0xecf3d3a7c1693e2dull: value->team = TEAM_BLUE; break; + case 0xcf00d78fd5953f1cull: value->team = TEAM_GREEN; break; + case 0xc416c97e0d3218b3ull: value->team = TEAM_GOLD; break; + default: value->team = TEAM_NONE; r->report->unknown++; break; + } } } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_cosmetic_prop_load( RenderCosmeticProp * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_cosmetic_prop_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_cosmetic_prop_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_laser_measure( const RenderLaser * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_start = render_vector3_measure( &value->start ); - if ( body_start < 0 ) { return -1; } - if ( body_start > 2 ) { bytes += 3 + 4 + body_start; } /* start: all-default nested elides */ - } - { - int64_t body_finish = render_vector3_measure( &value->finish ); - if ( body_finish < 0 ) { return -1; } - if ( body_finish > 2 ) { bytes += 3 + 4 + body_finish; } /* finish: all-default nested elides */ - } - if ( value->t != 0.0 ) { bytes += 3 + 8; } /* t */ - if ( value->laser_id != 0 ) { bytes += 3 + 4; } /* laser_id */ - if ( value->laser_type != LASER_TYPE_NONE ) - { - uint16_t id_laser_type = 0; - switch ( value->laser_type ) /* LaserType rides as the u16 hash of its VARIANT NAME (§5) */ +static SCHEMA_UNUSED int render_cosmetic_prop_save_message_body(TableBitWriter * w,const RenderCosmeticProp * value) +{ + (void)value; + { /* position */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->position)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->position)) return 0; + } + } + { /* rotation */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_quaternion_save_message_body(&probe,&value->rotation)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + if(!render_quaternion_save_message_body(w,&value->rotation)) return 0; + } + } + { /* scale */ + if(!(value->scale == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,19,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->scale),64); + } + } + { /* flags */ + if(!(value->flags == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,20,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->flags)-UINT64_C(0),64); + } + } + { /* cosmetic_prop_id */ + if(!(value->cosmetic_prop_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,21,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->cosmetic_prop_id)-UINT64_C(0),32); + } + } + { /* prop_sequence */ + if(!(value->prop_sequence == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,22,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->prop_sequence)-UINT64_C(0),8); + } + } + { /* prop_type */ + if(!(value->prop_type == PROP_TYPE_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,23,kTableMessageRefBitsHere); + switch(value->prop_type) { + case PROP_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case PROP_TYPE_ROCK: table_bit_put(w,63,kTableMessageRefBitsHere); + break; + case PROP_TYPE_STATION: table_bit_put(w,64,kTableMessageRefBitsHere); + break; + case PROP_TYPE_BEACON: table_bit_put(w,65,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* team */ + if(!(value->team == TEAM_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + switch(value->team) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_cosmetic_prop_measure_messages(const RenderCosmeticProp * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x4cbf3a26fca1d74a): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->position))return 0; + break; + } + case UINT64_C(0xb51afb05cd34709f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_quaternion_load_message_body(r,&value->rotation))return 0; + break; + } + case UINT64_C(0x6aacb9fbb71a1d91): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->scale=decoded; + } + break; + } + case UINT64_C(0x17a3a1a985f75aec): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->flags=(uint64_t)decoded; + } + break; + } + case UINT64_C(0xb66e4449e56b2e26): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->cosmetic_prop_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x4d7bf73bcbddc228): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->prop_sequence=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xe5626f155e4c5dad): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->prop_type=PROP_TYPE_NONE; + else switch(named->id) { + case UINT64_C(0xcaa6172bef74e234): value->prop_type=PROP_TYPE_ROCK; + break; + case UINT64_C(0x4f98acac2852d653): value->prop_type=PROP_TYPE_STATION; + break; + case UINT64_C(0x7f3a7e4744ea3019): value->prop_type=PROP_TYPE_BEACON; + break; + default:value->prop_type=PROP_TYPE_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0xfa23d9ef19afc32c): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->team=TEAM_NONE; + else switch(named->id) { + case UINT64_C(0x9ff1de19feac1b7c): value->team=TEAM_RED; + break; + case UINT64_C(0xecf3d3a7c1693e2d): value->team=TEAM_BLUE; + break; + case UINT64_C(0xcf00d78fd5953f1c): value->team=TEAM_GREEN; + break; + case UINT64_C(0xc416c97e0d3218b3): value->team=TEAM_GOLD; + break; + default:value->team=TEAM_NONE; + r->report->unknown++; + break; + } } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_cosmetic_prop_load_messages(RenderCosmeticProp * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_laser_save_body( TableWriter * w, const RenderLaser * value ) +{ + (void) value; + { /* start */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->start ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case LASER_TYPE_NONE: id_laser_type = 0; break; - case LASER_TYPE_PULSE: id_laser_type = 0xc187; break; - case LASER_TYPE_BEAM: id_laser_type = 0x1f28; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xee5d97ad45ad251full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->start ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->start ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->start ) ) { return 0; } + } } - (void) id_laser_type; - bytes += 3 + 2; /* laser_type: the variant's name hash */ } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* finish */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->finish ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6917a5bab91540f2ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->finish ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->finish ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->finish ) ) { return 0; } + } } - (void) id_team; - bytes += 3 + 2; /* team: the variant's name hash */ } - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_laser_save_body( TableWriter * w, const RenderLaser * value ) -{ - { - int64_t body_start = render_vector3_measure( &value->start ); - if ( body_start < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_start > 2 ) /* all-default nested elides */ + { /* t */ + if ( !( value->t == 0.0 ) ) { - table_writer_put16( w, 0x61f4 ); table_writer_put8( w, 13 ); /* start */ - table_writer_put32( w, (uint32_t) body_start ); - if ( !render_vector3_save_body( w, &value->start ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63e94c860202a3ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->t ) ); } } - { - int64_t body_finish = render_vector3_measure( &value->finish ); - if ( body_finish < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_finish > 2 ) /* all-default nested elides */ + { /* laser_id */ + if ( !( value->laser_id == 0 ) ) { - table_writer_put16( w, 0x2d84 ); table_writer_put8( w, 13 ); /* finish */ - table_writer_put32( w, (uint32_t) body_finish ); - if ( !render_vector3_save_body( w, &value->finish ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xcd29c05f390294ecull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->laser_id ) ); } } - if ( value->t != 0.0 ) - { - table_writer_put16( w, 0xccaf ); table_writer_put8( w, 11 ); /* t */ - table_writer_put64( w, table_double_to_bits( value->t ) ); - } - if ( value->laser_id != 0 ) - { - table_writer_put16( w, 0xc5ca ); table_writer_put8( w, 8 ); /* laser_id */ - table_writer_put32( w, (uint32_t) ( value->laser_id ) ); - } - if ( value->laser_type != LASER_TYPE_NONE ) - { - uint16_t id_laser_type = 0; - switch ( value->laser_type ) /* LaserType rides as the u16 hash of its VARIANT NAME (§5) */ + { /* laser_type */ + if ( !( value->laser_type == LASER_TYPE_NONE ) ) { - case LASER_TYPE_NONE: id_laser_type = 0; break; - case LASER_TYPE_PULSE: id_laser_type = 0xc187; break; - case LASER_TYPE_BEAM: id_laser_type = 0x1f28; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x96b593c242133841ull ); table_writer_put8( w, 30 ); + switch ( value->laser_type ) + { + case LASER_TYPE_NONE: table_writer_leb( w, 0 ); break; + case LASER_TYPE_PULSE: table_writer_id( w, 0x94e8a172d8f4805eull ); break; + case LASER_TYPE_BEAM: table_writer_id( w, 0xa0745aa7967eeffaull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xc46a ); table_writer_put8( w, 7 ); /* laser_type */ - table_writer_put16( w, id_laser_type ); } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* team */ + if ( !( value->team == TEAM_NONE ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfa23d9ef19afc32cull ); table_writer_put8( w, 30 ); + switch ( value->team ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xdff1 ); table_writer_put8( w, 7 ); /* team */ - table_writer_put16( w, id_team ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_laser_measure( const RenderLaser * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_laser_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_laser_save( const RenderLaser * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_laser_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_laser_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_laser_load_body( TableReader * r, RenderLaser * value ) +static SCHEMA_UNUSED int render_laser_load_body( TableReader * r, RenderLaser * value ) { - render_laser_reset( value ); /* prefill declared defaults in place, then overlay */ + render_laser_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x61f4: /* start */ + case 0xee5d97ad45ad251full: /* start */ { if ( kind != 13 ) { @@ -3092,18 +7337,13 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_laser_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->start ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->start ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->start ); } break; } - case 0x2d84: /* finish */ + case 0x6917a5bab91540f2ull: /* finish */ { if ( kind != 13 ) { @@ -3111,245 +7351,604 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_laser_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->finish ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->finish ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->finish ); } break; } - case 0xccaf: /* t */ + case 0xaf63e94c860202a3ull: /* t */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->t = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->t = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->t = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->t = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->t = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xc5ca: /* laser_id */ + case 0xcd29c05f390294ecull: /* laser_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->laser_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->laser_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->laser_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->laser_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->laser_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->laser_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->laser_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xc46a: /* laser_type */ + case 0x96b593c242133841ull: /* laser_type */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->laser_type = LASER_TYPE_NONE; } + else { - case 0: value->laser_type = LASER_TYPE_NONE; break; - case 0xc187: value->laser_type = LASER_TYPE_PULSE; break; - case 0x1f28: value->laser_type = LASER_TYPE_BEAM; break; - default: value->laser_type = LASER_TYPE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x94e8a172d8f4805eull: value->laser_type = LASER_TYPE_PULSE; break; + case 0xa0745aa7967eeffaull: value->laser_type = LASER_TYPE_BEAM; break; + default: value->laser_type = LASER_TYPE_NONE; r->report->unknown++; break; + } } } break; } - case 0xdff1: /* team */ + case 0xfa23d9ef19afc32cull: /* team */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->team = TEAM_NONE; } + else { - case 0: value->team = TEAM_NONE; break; - case 0xbb03: value->team = TEAM_RED; break; - case 0xf630: value->team = TEAM_BLUE; break; - case 0x6e0f: value->team = TEAM_GREEN; break; - case 0xda27: value->team = TEAM_GOLD; break; - default: value->team = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x9ff1de19feac1b7cull: value->team = TEAM_RED; break; + case 0xecf3d3a7c1693e2dull: value->team = TEAM_BLUE; break; + case 0xcf00d78fd5953f1cull: value->team = TEAM_GREEN; break; + case 0xc416c97e0d3218b3ull: value->team = TEAM_GOLD; break; + default: value->team = TEAM_NONE; r->report->unknown++; break; + } } } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_laser_load( RenderLaser * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_laser_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_laser_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_explosion_measure( const RenderExplosion * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return -1; } - if ( body_position > 2 ) { bytes += 3 + 4 + body_position; } /* position: all-default nested elides */ - } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return -1; } - if ( body_rotation > 2 ) { bytes += 3 + 4 + body_rotation; } /* rotation: all-default nested elides */ - } - if ( value->t != 0.0 ) { bytes += 3 + 8; } /* t */ - if ( value->explosion_id != 0 ) { bytes += 3 + 4; } /* explosion_id */ - if ( value->parent_object_id != 0 ) { bytes += 3 + 4; } /* parent_object_id */ - if ( value->explosion_type != EXPLOSION_TYPE_NONE ) - { - uint16_t id_explosion_type = 0; - switch ( value->explosion_type ) /* ExplosionType rides as the u16 hash of its VARIANT NAME (§5) */ +static SCHEMA_UNUSED int render_laser_save_message_body(TableBitWriter * w,const RenderLaser * value) +{ + (void)value; + { /* start */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->start)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,41,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->start)) return 0; + } + } + { /* finish */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->finish)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,42,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->finish)) return 0; + } + } + { /* t */ + if(!(value->t == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,27,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->t),64); + } + } + { /* laser_id */ + if(!(value->laser_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,43,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->laser_id)-UINT64_C(0),32); + } + } + { /* laser_type */ + if(!(value->laser_type == LASER_TYPE_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,44,kTableMessageRefBitsHere); + switch(value->laser_type) { + case LASER_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case LASER_TYPE_PULSE: table_bit_put(w,59,kTableMessageRefBitsHere); + break; + case LASER_TYPE_BEAM: table_bit_put(w,60,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* team */ + if(!(value->team == TEAM_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + switch(value->team) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_laser_measure_messages(const RenderLaser * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xee5d97ad45ad251f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->start))return 0; + break; + } + case UINT64_C(0x6917a5bab91540f2): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->finish))return 0; + break; + } + case UINT64_C(0xaf63e94c860202a3): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->t=decoded; + } + break; + } + case UINT64_C(0xcd29c05f390294ec): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->laser_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x96b593c242133841): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->laser_type=LASER_TYPE_NONE; + else switch(named->id) { + case UINT64_C(0x94e8a172d8f4805e): value->laser_type=LASER_TYPE_PULSE; + break; + case UINT64_C(0xa0745aa7967eeffa): value->laser_type=LASER_TYPE_BEAM; + break; + default:value->laser_type=LASER_TYPE_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0xfa23d9ef19afc32c): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->team=TEAM_NONE; + else switch(named->id) { + case UINT64_C(0x9ff1de19feac1b7c): value->team=TEAM_RED; + break; + case UINT64_C(0xecf3d3a7c1693e2d): value->team=TEAM_BLUE; + break; + case UINT64_C(0xcf00d78fd5953f1c): value->team=TEAM_GREEN; + break; + case UINT64_C(0xc416c97e0d3218b3): value->team=TEAM_GOLD; + break; + default:value->team=TEAM_NONE; + r->report->unknown++; + break; + } } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_laser_load_messages(RenderLaser * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_explosion_save_body( TableWriter * w, const RenderExplosion * value ) +{ + (void) value; + { /* position */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_vector3_save_body( &default_probe, &value->position ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case EXPLOSION_TYPE_NONE: id_explosion_type = 0; break; - case EXPLOSION_TYPE_SMALL: id_explosion_type = 0x2eac; break; - case EXPLOSION_TYPE_LARGE: id_explosion_type = 0x6edf; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4cbf3a26fca1d74aull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_vector3_save_body( &probe, &value->position ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + } } - (void) id_explosion_type; - bytes += 3 + 2; /* explosion_type: the variant's name hash */ } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* rotation */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !render_quaternion_save_body( &default_probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb51afb05cd34709full ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_quaternion_save_body( &probe, &value->rotation ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + } } - (void) id_team; - bytes += 3 + 2; /* team: the variant's name hash */ } - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_explosion_save_body( TableWriter * w, const RenderExplosion * value ) -{ - { - int64_t body_position = render_vector3_measure( &value->position ); - if ( body_position < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_position > 2 ) /* all-default nested elides */ + { /* t */ + if ( !( value->t == 0.0 ) ) { - table_writer_put16( w, 0xdd45 ); table_writer_put8( w, 13 ); /* position */ - table_writer_put32( w, (uint32_t) body_position ); - if ( !render_vector3_save_body( w, &value->position ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63e94c860202a3ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->t ) ); } } - { - int64_t body_rotation = render_quaternion_measure( &value->rotation ); - if ( body_rotation < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_rotation > 2 ) /* all-default nested elides */ + { /* explosion_id */ + if ( !( value->explosion_id == 0 ) ) { - table_writer_put16( w, 0x60f3 ); table_writer_put8( w, 13 ); /* rotation */ - table_writer_put32( w, (uint32_t) body_rotation ); - if ( !render_quaternion_save_body( w, &value->rotation ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfd7f3fa0b16ed778ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->explosion_id ) ); } } - if ( value->t != 0.0 ) - { - table_writer_put16( w, 0xccaf ); table_writer_put8( w, 11 ); /* t */ - table_writer_put64( w, table_double_to_bits( value->t ) ); - } - if ( value->explosion_id != 0 ) - { - table_writer_put16( w, 0x5be0 ); table_writer_put8( w, 8 ); /* explosion_id */ - table_writer_put32( w, (uint32_t) ( value->explosion_id ) ); - } - if ( value->parent_object_id != 0 ) - { - table_writer_put16( w, 0xeeb6 ); table_writer_put8( w, 8 ); /* parent_object_id */ - table_writer_put32( w, (uint32_t) ( value->parent_object_id ) ); + { /* parent_object_id */ + if ( !( value->parent_object_id == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0bee7b3cb4046c93ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->parent_object_id ) ); + } } - if ( value->explosion_type != EXPLOSION_TYPE_NONE ) - { - uint16_t id_explosion_type = 0; - switch ( value->explosion_type ) /* ExplosionType rides as the u16 hash of its VARIANT NAME (§5) */ + { /* explosion_type */ + if ( !( value->explosion_type == EXPLOSION_TYPE_NONE ) ) { - case EXPLOSION_TYPE_NONE: id_explosion_type = 0; break; - case EXPLOSION_TYPE_SMALL: id_explosion_type = 0x2eac; break; - case EXPLOSION_TYPE_LARGE: id_explosion_type = 0x6edf; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xdc89eb9cd3393be5ull ); table_writer_put8( w, 30 ); + switch ( value->explosion_type ) + { + case EXPLOSION_TYPE_NONE: table_writer_leb( w, 0 ); break; + case EXPLOSION_TYPE_SMALL: table_writer_id( w, 0x3d2cc8d952adebecull ); break; + case EXPLOSION_TYPE_LARGE: table_writer_id( w, 0xc8736ef79380e634ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0x98fa ); table_writer_put8( w, 7 ); /* explosion_type */ - table_writer_put16( w, id_explosion_type ); } - if ( value->team != TEAM_NONE ) - { - uint16_t id_team = 0; - switch ( value->team ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ + { /* team */ + if ( !( value->team == TEAM_NONE ) ) { - case TEAM_NONE: id_team = 0; break; - case TEAM_RED: id_team = 0xbb03; break; - case TEAM_BLUE: id_team = 0xf630; break; - case TEAM_GREEN: id_team = 0x6e0f; break; - case TEAM_GOLD: id_team = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfa23d9ef19afc32cull ); table_writer_put8( w, 30 ); + switch ( value->team ) + { + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + case TEAM_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xdff1 ); table_writer_put8( w, 7 ); /* team */ - table_writer_put16( w, id_team ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_explosion_measure( const RenderExplosion * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_explosion_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_explosion_save( const RenderExplosion * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_explosion_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_explosion_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_explosion_load_body( TableReader * r, RenderExplosion * value ) +static SCHEMA_UNUSED int render_explosion_load_body( TableReader * r, RenderExplosion * value ) { - render_explosion_reset( value ); /* prefill declared defaults in place, then overlay */ + render_explosion_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xdd45: /* position */ + case 0x4cbf3a26fca1d74aull: /* position */ { if ( kind != 13 ) { @@ -3357,18 +7956,13 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_explosion_load_bod if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_vector3_load_body( &sub, &value->position ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_vector3_load_body( &sub, &value->position ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_vector3_reset( &value->position ); } break; } - case 0x60f3: /* rotation */ + case 0xb51afb05cd34709full: /* rotation */ { if ( kind != 13 ) { @@ -3376,456 +7970,1095 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_explosion_load_bod if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - render_quaternion_load_body( &sub, &value->rotation ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + render_quaternion_load_body( &sub, &value->rotation ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; render_quaternion_reset( &value->rotation ); } break; } - case 0xccaf: /* t */ + case 0xaf63e94c860202a3ull: /* t */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->t = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->t = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->t = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->t = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->t = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x5be0: /* explosion_id */ + case 0xfd7f3fa0b16ed778ull: /* explosion_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->explosion_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->explosion_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->explosion_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->explosion_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->explosion_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->explosion_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->explosion_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xeeb6: /* parent_object_id */ + case 0x0bee7b3cb4046c93ull: /* parent_object_id */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->parent_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->parent_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->parent_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->parent_object_id = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->parent_object_id = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->parent_object_id = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->parent_object_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x98fa: /* explosion_type */ + case 0xdc89eb9cd3393be5ull: /* explosion_type */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->explosion_type = EXPLOSION_TYPE_NONE; } + else { - case 0: value->explosion_type = EXPLOSION_TYPE_NONE; break; - case 0x2eac: value->explosion_type = EXPLOSION_TYPE_SMALL; break; - case 0x6edf: value->explosion_type = EXPLOSION_TYPE_LARGE; break; - default: value->explosion_type = EXPLOSION_TYPE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x3d2cc8d952adebecull: value->explosion_type = EXPLOSION_TYPE_SMALL; break; + case 0xc8736ef79380e634ull: value->explosion_type = EXPLOSION_TYPE_LARGE; break; + default: value->explosion_type = EXPLOSION_TYPE_NONE; r->report->unknown++; break; + } } } break; } - case 0xdff1: /* team */ + case 0xfa23d9ef19afc32cull: /* team */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->team = TEAM_NONE; } + else { - case 0: value->team = TEAM_NONE; break; - case 0xbb03: value->team = TEAM_RED; break; - case 0xf630: value->team = TEAM_BLUE; break; - case 0x6e0f: value->team = TEAM_GREEN; break; - case 0xda27: value->team = TEAM_GOLD; break; - default: value->team = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x9ff1de19feac1b7cull: value->team = TEAM_RED; break; + case 0xecf3d3a7c1693e2dull: value->team = TEAM_BLUE; break; + case 0xcf00d78fd5953f1cull: value->team = TEAM_GREEN; break; + case 0xc416c97e0d3218b3ull: value->team = TEAM_GOLD; break; + default: value->team = TEAM_NONE; r->report->unknown++; break; + } } } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_explosion_load( RenderExplosion * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_explosion_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_explosion_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_frame_measure( const RenderFrame * value ) +static SCHEMA_UNUSED int render_explosion_save_message_body(TableBitWriter * w,const RenderExplosion * value) +{ + (void)value; + { /* position */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_vector3_save_message_body(&probe,&value->position)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + if(!render_vector3_save_message_body(w,&value->position)) return 0; + } + } + { /* rotation */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!render_quaternion_save_message_body(&probe,&value->rotation)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + if(!render_quaternion_save_message_body(w,&value->rotation)) return 0; + } + } + { /* t */ + if(!(value->t == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,27,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->t),64); + } + } + { /* explosion_id */ + if(!(value->explosion_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,28,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->explosion_id)-UINT64_C(0),32); + } + } + { /* parent_object_id */ + if(!(value->parent_object_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,29,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->parent_object_id)-UINT64_C(0),32); + } + } + { /* explosion_type */ + if(!(value->explosion_type == EXPLOSION_TYPE_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,30,kTableMessageRefBitsHere); + switch(value->explosion_type) { + case EXPLOSION_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case EXPLOSION_TYPE_SMALL: table_bit_put(w,57,kTableMessageRefBitsHere); + break; + case EXPLOSION_TYPE_LARGE: table_bit_put(w,58,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* team */ + if(!(value->team == TEAM_NONE)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + switch(value->team) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,69,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,70,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,71,kTableMessageRefBitsHere); + break; + case TEAM_GOLD: table_bit_put(w,72,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_explosion_measure_messages(const RenderExplosion * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x4cbf3a26fca1d74a): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_vector3_load_message_body(r,&value->position))return 0; + break; + } + case UINT64_C(0xb51afb05cd34709f): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!render_quaternion_load_message_body(r,&value->rotation))return 0; + break; + } + case UINT64_C(0xaf63e94c860202a3): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->t=decoded; + } + break; + } + case UINT64_C(0xfd7f3fa0b16ed778): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->explosion_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x0bee7b3cb4046c93): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->parent_object_id=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xdc89eb9cd3393be5): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->explosion_type=EXPLOSION_TYPE_NONE; + else switch(named->id) { + case UINT64_C(0x3d2cc8d952adebec): value->explosion_type=EXPLOSION_TYPE_SMALL; + break; + case UINT64_C(0xc8736ef79380e634): value->explosion_type=EXPLOSION_TYPE_LARGE; + break; + default:value->explosion_type=EXPLOSION_TYPE_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0xfa23d9ef19afc32c): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->team=TEAM_NONE; + else switch(named->id) { + case UINT64_C(0x9ff1de19feac1b7c): value->team=TEAM_RED; + break; + case UINT64_C(0xecf3d3a7c1693e2d): value->team=TEAM_BLUE; + break; + case UINT64_C(0xcf00d78fd5953f1c): value->team=TEAM_GREEN; + break; + case UINT64_C(0xc416c97e0d3218b3): value->team=TEAM_GOLD; + break; + default:value->team=TEAM_NONE; + r->report->unknown++; + break; + } } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_explosion_load_messages(RenderExplosion * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_cameras_( TableWriter * w, const RenderFrame * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->version != 0 ) { bytes += 3 + 8; } /* version */ - if ( value->cameras_count < 0 || value->cameras_count > 1 ) { return -1; } /* storage invariant */ - if ( value->cameras_count > 0 ) + if ( value->cameras_count < 0 || value->cameras_count > 1 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->cameras_count ); + for ( i = 0; i < value->cameras_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* cameras */ - for ( i = 0; i < value->cameras_count; i++ ) + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_camera_save_body( w, &value->cameras[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_cameras = render_camera_measure( &value->cameras[i] ); - if ( elem_cameras < 0 ) { return -1; } - bytes += 4 + elem_cameras; + TableWriter probe = table_writer_probe( w ); + if ( !render_camera_save_body( &probe, &value->cameras[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_camera_save_body( w, &value->cameras[i] ) ) { return 0; } } } - if ( value->ships_count < 0 || value->ships_count > 4096 ) { return -1; } /* storage invariant */ - if ( value->ships_count > 0 ) + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_ships_( TableWriter * w, const RenderFrame * value ) +{ + if ( value->ships_count < 0 || value->ships_count > 4096 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->ships_count ); + for ( i = 0; i < value->ships_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* ships */ - for ( i = 0; i < value->ships_count; i++ ) + if ( w->buffer == NULL ) { - int64_t elem_ships = render_ship_measure( &value->ships[i] ); - if ( elem_ships < 0 ) { return -1; } - bytes += 4 + elem_ships; + int64_t frame_begin = w->offset; + if ( !render_ship_save_body( w, &value->ships[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_ship_save_body( &probe, &value->ships[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_ship_save_body( w, &value->ships[i] ) ) { return 0; } } } - if ( value->turrets_count < 0 || value->turrets_count > 1024 ) { return -1; } /* storage invariant */ - if ( value->turrets_count > 0 ) + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_turrets_( TableWriter * w, const RenderFrame * value ) +{ + if ( value->turrets_count < 0 || value->turrets_count > 1024 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->turrets_count ); + for ( i = 0; i < value->turrets_count; i++ ) + { + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_turret_save_body( w, &value->turrets[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_turret_save_body( &probe, &value->turrets[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_turret_save_body( w, &value->turrets[i] ) ) { return 0; } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_missiles_( TableWriter * w, const RenderFrame * value ) +{ + if ( value->missiles_count < 0 || value->missiles_count > 4096 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->missiles_count ); + for ( i = 0; i < value->missiles_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* turrets */ - for ( i = 0; i < value->turrets_count; i++ ) + if ( w->buffer == NULL ) { - int64_t elem_turrets = render_turret_measure( &value->turrets[i] ); - if ( elem_turrets < 0 ) { return -1; } - bytes += 4 + elem_turrets; + int64_t frame_begin = w->offset; + if ( !render_missile_save_body( w, &value->missiles[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_missile_save_body( &probe, &value->missiles[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_missile_save_body( w, &value->missiles[i] ) ) { return 0; } } } - if ( value->missiles_count < 0 || value->missiles_count > 4096 ) { return -1; } /* storage invariant */ - if ( value->missiles_count > 0 ) + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_dynamic_props_( TableWriter * w, const RenderFrame * value ) +{ + if ( value->dynamic_props_count < 0 || value->dynamic_props_count > 4096 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->dynamic_props_count ); + for ( i = 0; i < value->dynamic_props_count; i++ ) + { + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_dynamic_prop_save_body( w, &value->dynamic_props[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_dynamic_prop_save_body( &probe, &value->dynamic_props[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_dynamic_prop_save_body( w, &value->dynamic_props[i] ) ) { return 0; } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_static_props_( TableWriter * w, const RenderFrame * value ) +{ + if ( value->static_props_count < 0 || value->static_props_count > 20000 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->static_props_count ); + for ( i = 0; i < value->static_props_count; i++ ) + { + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !render_static_prop_save_body( w, &value->static_props[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !render_static_prop_save_body( &probe, &value->static_props[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_static_prop_save_body( w, &value->static_props[i] ) ) { return 0; } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_cosmetic_props_( TableWriter * w, const RenderFrame * value ) +{ + if ( value->cosmetic_props_count < 0 || value->cosmetic_props_count > 8192 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->cosmetic_props_count ); + for ( i = 0; i < value->cosmetic_props_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* missiles */ - for ( i = 0; i < value->missiles_count; i++ ) + if ( w->buffer == NULL ) { - int64_t elem_missiles = render_missile_measure( &value->missiles[i] ); - if ( elem_missiles < 0 ) { return -1; } - bytes += 4 + elem_missiles; + int64_t frame_begin = w->offset; + if ( !render_cosmetic_prop_save_body( w, &value->cosmetic_props[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - } - if ( value->dynamic_props_count < 0 || value->dynamic_props_count > 4096 ) { return -1; } /* storage invariant */ - if ( value->dynamic_props_count > 0 ) - { - int32_t i; - bytes += 3 + 4 + 5; /* dynamic_props */ - for ( i = 0; i < value->dynamic_props_count; i++ ) + else { - int64_t elem_dynamic_props = render_dynamic_prop_measure( &value->dynamic_props[i] ); - if ( elem_dynamic_props < 0 ) { return -1; } - bytes += 4 + elem_dynamic_props; + TableWriter probe = table_writer_probe( w ); + if ( !render_cosmetic_prop_save_body( &probe, &value->cosmetic_props[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_cosmetic_prop_save_body( w, &value->cosmetic_props[i] ) ) { return 0; } } } - if ( value->static_props_count < 0 || value->static_props_count > 20000 ) { return -1; } /* storage invariant */ - if ( value->static_props_count > 0 ) + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_lasers_( TableWriter * w, const RenderFrame * value ) +{ + if ( value->lasers_count < 0 || value->lasers_count > 32000 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->lasers_count ); + for ( i = 0; i < value->lasers_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* static_props */ - for ( i = 0; i < value->static_props_count; i++ ) + if ( w->buffer == NULL ) { - int64_t elem_static_props = render_static_prop_measure( &value->static_props[i] ); - if ( elem_static_props < 0 ) { return -1; } - bytes += 4 + elem_static_props; + int64_t frame_begin = w->offset; + if ( !render_laser_save_body( w, &value->lasers[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - } - if ( value->cosmetic_props_count < 0 || value->cosmetic_props_count > 8192 ) { return -1; } /* storage invariant */ - if ( value->cosmetic_props_count > 0 ) - { - int32_t i; - bytes += 3 + 4 + 5; /* cosmetic_props */ - for ( i = 0; i < value->cosmetic_props_count; i++ ) + else { - int64_t elem_cosmetic_props = render_cosmetic_prop_measure( &value->cosmetic_props[i] ); - if ( elem_cosmetic_props < 0 ) { return -1; } - bytes += 4 + elem_cosmetic_props; + TableWriter probe = table_writer_probe( w ); + if ( !render_laser_save_body( &probe, &value->lasers[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_laser_save_body( w, &value->lasers[i] ) ) { return 0; } } } - if ( value->lasers_count < 0 || value->lasers_count > 32000 ) { return -1; } /* storage invariant */ - if ( value->lasers_count > 0 ) + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_blockdemo_render_frame_wire_explosions_( TableWriter * w, const RenderFrame * value ) +{ + if ( value->explosions_count < 0 || value->explosions_count > 32000 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->explosions_count ); + for ( i = 0; i < value->explosions_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* lasers */ - for ( i = 0; i < value->lasers_count; i++ ) + if ( w->buffer == NULL ) { - int64_t elem_lasers = render_laser_measure( &value->lasers[i] ); - if ( elem_lasers < 0 ) { return -1; } - bytes += 4 + elem_lasers; + int64_t frame_begin = w->offset; + if ( !render_explosion_save_body( w, &value->explosions[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - } - if ( value->explosions_count < 0 || value->explosions_count > 32000 ) { return -1; } /* storage invariant */ - if ( value->explosions_count > 0 ) - { - int32_t i; - bytes += 3 + 4 + 5; /* explosions */ - for ( i = 0; i < value->explosions_count; i++ ) + else { - int64_t elem_explosions = render_explosion_measure( &value->explosions[i] ); - if ( elem_explosions < 0 ) { return -1; } - bytes += 4 + elem_explosions; + TableWriter probe = table_writer_probe( w ); + if ( !render_explosion_save_body( &probe, &value->explosions[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !render_explosion_save_body( w, &value->explosions[i] ) ) { return 0; } } } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_save_body( TableWriter * w, const RenderFrame * value ) +static SCHEMA_UNUSED int render_frame_save_body( TableWriter * w, const RenderFrame * value ) { - if ( value->version != 0 ) - { - table_writer_put16( w, 0xe8e6 ); table_writer_put8( w, 9 ); /* version */ - table_writer_put64( w, (uint64_t) ( value->version ) ); + (void) value; + { /* version */ + if ( !( value->version == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbb62c62c9808ea37ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->version ) ); + } } - if ( value->cameras_count < 0 || value->cameras_count > 1 ) { return 0; } /* storage invariant */ - if ( value->cameras_count > 0 ) - { - int64_t len_at_cameras; - int32_t i; - table_writer_put16( w, 0x8b0e ); table_writer_put8( w, 14 ); /* cameras */ - len_at_cameras = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->cameras_count ); - for ( i = 0; i < value->cameras_count; i++ ) + { /* cameras */ + if ( value->cameras_count < 0 || value->cameras_count > 1 ) { return 0; } + if ( value->cameras_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0f9222d2ba7aa2a7ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_cameras_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_camera_save_body( w, &value->cameras[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_cameras_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_cameras_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_cameras, (uint32_t) ( w->offset - len_at_cameras - 4 ) ); } - if ( value->ships_count < 0 || value->ships_count > 4096 ) { return 0; } /* storage invariant */ - if ( value->ships_count > 0 ) - { - int64_t len_at_ships; - int32_t i; - table_writer_put16( w, 0x2d39 ); table_writer_put8( w, 14 ); /* ships */ - len_at_ships = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->ships_count ); - for ( i = 0; i < value->ships_count; i++ ) + { /* ships */ + if ( value->ships_count < 0 || value->ships_count > 4096 ) { return 0; } + if ( value->ships_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x294a5c4913e1ad44ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_ship_save_body( w, &value->ships[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_ships_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_ships_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_ships_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_ships, (uint32_t) ( w->offset - len_at_ships - 4 ) ); } - if ( value->turrets_count < 0 || value->turrets_count > 1024 ) { return 0; } /* storage invariant */ - if ( value->turrets_count > 0 ) - { - int64_t len_at_turrets; - int32_t i; - table_writer_put16( w, 0x48ad ); table_writer_put8( w, 14 ); /* turrets */ - len_at_turrets = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->turrets_count ); - for ( i = 0; i < value->turrets_count; i++ ) + { /* turrets */ + if ( value->turrets_count < 0 || value->turrets_count > 1024 ) { return 0; } + if ( value->turrets_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x84f8260bc283608cull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_turrets_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_turret_save_body( w, &value->turrets[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_turrets_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_turrets_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_turrets, (uint32_t) ( w->offset - len_at_turrets - 4 ) ); } - if ( value->missiles_count < 0 || value->missiles_count > 4096 ) { return 0; } /* storage invariant */ - if ( value->missiles_count > 0 ) - { - int64_t len_at_missiles; - int32_t i; - table_writer_put16( w, 0xa532 ); table_writer_put8( w, 14 ); /* missiles */ - len_at_missiles = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->missiles_count ); - for ( i = 0; i < value->missiles_count; i++ ) + { /* missiles */ + if ( value->missiles_count < 0 || value->missiles_count > 4096 ) { return 0; } + if ( value->missiles_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1c3027194b1ba4d0ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_missiles_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_missile_save_body( w, &value->missiles[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_missiles_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_missiles_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_missiles, (uint32_t) ( w->offset - len_at_missiles - 4 ) ); } - if ( value->dynamic_props_count < 0 || value->dynamic_props_count > 4096 ) { return 0; } /* storage invariant */ - if ( value->dynamic_props_count > 0 ) - { - int64_t len_at_dynamic_props; - int32_t i; - table_writer_put16( w, 0x810b ); table_writer_put8( w, 14 ); /* dynamic_props */ - len_at_dynamic_props = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->dynamic_props_count ); - for ( i = 0; i < value->dynamic_props_count; i++ ) + { /* dynamic_props */ + if ( value->dynamic_props_count < 0 || value->dynamic_props_count > 4096 ) { return 0; } + if ( value->dynamic_props_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x43125398a9903d27ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_dynamic_props_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_dynamic_prop_save_body( w, &value->dynamic_props[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_dynamic_props_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_dynamic_props_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_dynamic_props, (uint32_t) ( w->offset - len_at_dynamic_props - 4 ) ); } - if ( value->static_props_count < 0 || value->static_props_count > 20000 ) { return 0; } /* storage invariant */ - if ( value->static_props_count > 0 ) - { - int64_t len_at_static_props; - int32_t i; - table_writer_put16( w, 0x55a9 ); table_writer_put8( w, 14 ); /* static_props */ - len_at_static_props = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->static_props_count ); - for ( i = 0; i < value->static_props_count; i++ ) + { /* static_props */ + if ( value->static_props_count < 0 || value->static_props_count > 20000 ) { return 0; } + if ( value->static_props_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc1f8d7edff7fcfd2ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_static_prop_save_body( w, &value->static_props[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_static_props_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_static_props_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_static_props_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_static_props, (uint32_t) ( w->offset - len_at_static_props - 4 ) ); } - if ( value->cosmetic_props_count < 0 || value->cosmetic_props_count > 8192 ) { return 0; } /* storage invariant */ - if ( value->cosmetic_props_count > 0 ) - { - int64_t len_at_cosmetic_props; - int32_t i; - table_writer_put16( w, 0x1142 ); table_writer_put8( w, 14 ); /* cosmetic_props */ - len_at_cosmetic_props = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->cosmetic_props_count ); - for ( i = 0; i < value->cosmetic_props_count; i++ ) + { /* cosmetic_props */ + if ( value->cosmetic_props_count < 0 || value->cosmetic_props_count > 8192 ) { return 0; } + if ( value->cosmetic_props_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x33408e39f5f480ffull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_cosmetic_props_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_cosmetic_prop_save_body( w, &value->cosmetic_props[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_cosmetic_props_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_cosmetic_props_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_cosmetic_props, (uint32_t) ( w->offset - len_at_cosmetic_props - 4 ) ); } - if ( value->lasers_count < 0 || value->lasers_count > 32000 ) { return 0; } /* storage invariant */ - if ( value->lasers_count > 0 ) - { - int64_t len_at_lasers; - int32_t i; - table_writer_put16( w, 0x411a ); table_writer_put8( w, 14 ); /* lasers */ - len_at_lasers = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->lasers_count ); - for ( i = 0; i < value->lasers_count; i++ ) + { /* lasers */ + if ( value->lasers_count < 0 || value->lasers_count > 32000 ) { return 0; } + if ( value->lasers_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd982b77b3e92d16dull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_lasers_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_laser_save_body( w, &value->lasers[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_lasers_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_lasers_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_lasers, (uint32_t) ( w->offset - len_at_lasers - 4 ) ); } - if ( value->explosions_count < 0 || value->explosions_count > 32000 ) { return 0; } /* storage invariant */ - if ( value->explosions_count > 0 ) - { - int64_t len_at_explosions; - int32_t i; - table_writer_put16( w, 0x6bfb ); table_writer_put8( w, 14 ); /* explosions */ - len_at_explosions = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->explosions_count ); - for ( i = 0; i < value->explosions_count; i++ ) + { /* explosions */ + if ( value->explosions_count < 0 || value->explosions_count > 32000 ) { return 0; } + if ( value->explosions_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x876a8c1a85806a69ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_blockdemo_render_frame_wire_explosions_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !render_explosion_save_body( w, &value->explosions[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_blockdemo_render_frame_wire_explosions_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_blockdemo_render_frame_wire_explosions_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_explosions, (uint32_t) ( w->offset - len_at_explosions - 4 ) ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_frame_measure( const RenderFrame * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_frame_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_frame_save( const RenderFrame * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_frame_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_frame_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( TableReader * r, RenderFrame * value ) +static SCHEMA_UNUSED int render_frame_load_body( TableReader * r, RenderFrame * value ) { - render_frame_reset( value ); /* prefill declared defaults in place, then overlay */ + render_frame_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xe8e6: /* version */ + case 0xbb62c62c9808ea37ull: /* version */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->version = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->version = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->version = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->version = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->version = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->version = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->version = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->version = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->version = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x8b0e: /* cameras */ + case 0x0f9222d2ba7aa2a7ull: /* cameras */ { if ( kind != 14 ) { @@ -3833,47 +9066,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 1 ) { keep = 1; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 1 ) { kept = 1; r->report->clamped++; } + int32_t previous_count = value->cameras_count; + value->cameras_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_cameras; } render_camera_load_body( &elem, &value->cameras[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_camera_reset(&value->cameras[i]); } + value->cameras_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_cameras: ; + { uint32_t tail; for (tail=(uint32_t)value->cameras_count;tail<(uint32_t)previous_count && tail<1;tail++) { + render_camera_reset(&value->cameras[tail]); + } } } - value->cameras_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x2d39: /* ships */ + case 0x294a5c4913e1ad44ull: /* ships */ { if ( kind != 14 ) { @@ -3881,47 +9106,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4096 ) { keep = 4096; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4096 ) { kept = 4096; r->report->clamped++; } + int32_t previous_count = value->ships_count; + value->ships_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_ships; } render_ship_load_body( &elem, &value->ships[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_ship_reset(&value->ships[i]); } + value->ships_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_ships: ; + { uint32_t tail; for (tail=(uint32_t)value->ships_count;tail<(uint32_t)previous_count && tail<4096;tail++) { + render_ship_reset(&value->ships[tail]); + } } } - value->ships_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x48ad: /* turrets */ + case 0x84f8260bc283608cull: /* turrets */ { if ( kind != 14 ) { @@ -3929,47 +9146,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 1024 ) { keep = 1024; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 1024 ) { kept = 1024; r->report->clamped++; } + int32_t previous_count = value->turrets_count; + value->turrets_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_turrets; } render_turret_load_body( &elem, &value->turrets[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_turret_reset(&value->turrets[i]); } + value->turrets_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_turrets: ; + { uint32_t tail; for (tail=(uint32_t)value->turrets_count;tail<(uint32_t)previous_count && tail<1024;tail++) { + render_turret_reset(&value->turrets[tail]); + } } } - value->turrets_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0xa532: /* missiles */ + case 0x1c3027194b1ba4d0ull: /* missiles */ { if ( kind != 14 ) { @@ -3977,47 +9186,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4096 ) { keep = 4096; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4096 ) { kept = 4096; r->report->clamped++; } + int32_t previous_count = value->missiles_count; + value->missiles_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_missiles; } render_missile_load_body( &elem, &value->missiles[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_missile_reset(&value->missiles[i]); } + value->missiles_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_missiles: ; + { uint32_t tail; for (tail=(uint32_t)value->missiles_count;tail<(uint32_t)previous_count && tail<4096;tail++) { + render_missile_reset(&value->missiles[tail]); + } } } - value->missiles_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x810b: /* dynamic_props */ + case 0x43125398a9903d27ull: /* dynamic_props */ { if ( kind != 14 ) { @@ -4025,47 +9226,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4096 ) { keep = 4096; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4096 ) { kept = 4096; r->report->clamped++; } + int32_t previous_count = value->dynamic_props_count; + value->dynamic_props_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_dynamic_props; } render_dynamic_prop_load_body( &elem, &value->dynamic_props[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_dynamic_prop_reset(&value->dynamic_props[i]); } + value->dynamic_props_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_dynamic_props: ; + { uint32_t tail; for (tail=(uint32_t)value->dynamic_props_count;tail<(uint32_t)previous_count && tail<4096;tail++) { + render_dynamic_prop_reset(&value->dynamic_props[tail]); + } } } - value->dynamic_props_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x55a9: /* static_props */ + case 0xc1f8d7edff7fcfd2ull: /* static_props */ { if ( kind != 14 ) { @@ -4073,47 +9266,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 20000 ) { keep = 20000; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 20000 ) { kept = 20000; r->report->clamped++; } + int32_t previous_count = value->static_props_count; + value->static_props_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_static_props; } render_static_prop_load_body( &elem, &value->static_props[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_static_prop_reset(&value->static_props[i]); } + value->static_props_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_static_props: ; + { uint32_t tail; for (tail=(uint32_t)value->static_props_count;tail<(uint32_t)previous_count && tail<20000;tail++) { + render_static_prop_reset(&value->static_props[tail]); + } } } - value->static_props_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x1142: /* cosmetic_props */ + case 0x33408e39f5f480ffull: /* cosmetic_props */ { if ( kind != 14 ) { @@ -4121,47 +9306,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 8192 ) { keep = 8192; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 8192 ) { kept = 8192; r->report->clamped++; } + int32_t previous_count = value->cosmetic_props_count; + value->cosmetic_props_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_cosmetic_props; } render_cosmetic_prop_load_body( &elem, &value->cosmetic_props[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_cosmetic_prop_reset(&value->cosmetic_props[i]); } + value->cosmetic_props_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_cosmetic_props: ; + { uint32_t tail; for (tail=(uint32_t)value->cosmetic_props_count;tail<(uint32_t)previous_count && tail<8192;tail++) { + render_cosmetic_prop_reset(&value->cosmetic_props[tail]); + } } } - value->cosmetic_props_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x411a: /* lasers */ + case 0xd982b77b3e92d16dull: /* lasers */ { if ( kind != 14 ) { @@ -4169,47 +9346,39 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 32000 ) { keep = 32000; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 32000 ) { kept = 32000; r->report->clamped++; } + int32_t previous_count = value->lasers_count; + value->lasers_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_lasers; } render_laser_load_body( &elem, &value->lasers[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_laser_reset(&value->lasers[i]); } + value->lasers_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_lasers: ; + { uint32_t tail; for (tail=(uint32_t)value->lasers_count;tail<(uint32_t)previous_count && tail<32000;tail++) { + render_laser_reset(&value->lasers[tail]); + } } } - value->lasers_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x6bfb: /* explosions */ + case 0x876a8c1a85806a69ull: /* explosions */ { if ( kind != 14 ) { @@ -4217,295 +9386,1483 @@ static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_frame_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 32000 ) { keep = 32000; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 32000 ) { kept = 32000; r->report->clamped++; } + int32_t previous_count = value->explosions_count; + value->explosions_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_explosions; } render_explosion_load_body( &elem, &value->explosions[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; render_explosion_reset(&value->explosions[i]); } + value->explosions_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_explosions: ; + { uint32_t tail; for (tail=(uint32_t)value->explosions_count;tail<(uint32_t)previous_count && tail<32000;tail++) { + render_explosion_reset(&value->explosions[tail]); + } } } - value->explosions_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_frame_load( RenderFrame * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_frame_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_frame_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_vector3_measure( const RenderVector3 * value ) -{ - int64_t bytes = 2; /* terminator */ - if ( value->x != 0.0 ) { bytes += 3 + 8; } /* x */ - if ( value->y != 0.0 ) { bytes += 3 + 8; } /* y */ - if ( value->z != 0.0 ) { bytes += 3 + 8; } /* z */ - return bytes; +static SCHEMA_UNUSED int render_frame_save_message_body(TableBitWriter * w,const RenderFrame * value) +{ + (void)value; + { /* version */ + if(!(value->version == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,31,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->version)-UINT64_C(0),64); + } + } + { /* cameras */ + if(value->cameras_count<0 || value->cameras_count>1) return 0; + if(value->cameras_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,32,kTableMessageRefBitsHere); + if(value->cameras_count<0 || value->cameras_count>1) return 0; + table_bit_put(w,(uint64_t)value->cameras_count-0,1); + { int32_t i; + for(i=0;icameras_count;i++) { + if(!render_camera_save_message_body(w,&value->cameras[i])) return 0; + } } + } + } + { /* ships */ + if(value->ships_count<0 || value->ships_count>4096) return 0; + if(value->ships_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,33,kTableMessageRefBitsHere); + if(value->ships_count<0 || value->ships_count>4096) return 0; + table_bit_put(w,(uint64_t)value->ships_count-0,13); + { int32_t i; + for(i=0;iships_count;i++) { + if(!render_ship_save_message_body(w,&value->ships[i])) return 0; + } } + } + } + { /* turrets */ + if(value->turrets_count<0 || value->turrets_count>1024) return 0; + if(value->turrets_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,34,kTableMessageRefBitsHere); + if(value->turrets_count<0 || value->turrets_count>1024) return 0; + table_bit_put(w,(uint64_t)value->turrets_count-0,11); + { int32_t i; + for(i=0;iturrets_count;i++) { + if(!render_turret_save_message_body(w,&value->turrets[i])) return 0; + } } + } + } + { /* missiles */ + if(value->missiles_count<0 || value->missiles_count>4096) return 0; + if(value->missiles_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,35,kTableMessageRefBitsHere); + if(value->missiles_count<0 || value->missiles_count>4096) return 0; + table_bit_put(w,(uint64_t)value->missiles_count-0,13); + { int32_t i; + for(i=0;imissiles_count;i++) { + if(!render_missile_save_message_body(w,&value->missiles[i])) return 0; + } } + } + } + { /* dynamic_props */ + if(value->dynamic_props_count<0 || value->dynamic_props_count>4096) return 0; + if(value->dynamic_props_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,36,kTableMessageRefBitsHere); + if(value->dynamic_props_count<0 || value->dynamic_props_count>4096) return 0; + table_bit_put(w,(uint64_t)value->dynamic_props_count-0,13); + { int32_t i; + for(i=0;idynamic_props_count;i++) { + if(!render_dynamic_prop_save_message_body(w,&value->dynamic_props[i])) return 0; + } } + } + } + { /* static_props */ + if(value->static_props_count<0 || value->static_props_count>20000) return 0; + if(value->static_props_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,37,kTableMessageRefBitsHere); + if(value->static_props_count<0 || value->static_props_count>20000) return 0; + table_bit_put(w,(uint64_t)value->static_props_count-0,15); + { int32_t i; + for(i=0;istatic_props_count;i++) { + if(!render_static_prop_save_message_body(w,&value->static_props[i])) return 0; + } } + } + } + { /* cosmetic_props */ + if(value->cosmetic_props_count<0 || value->cosmetic_props_count>8192) return 0; + if(value->cosmetic_props_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,38,kTableMessageRefBitsHere); + if(value->cosmetic_props_count<0 || value->cosmetic_props_count>8192) return 0; + table_bit_put(w,(uint64_t)value->cosmetic_props_count-0,14); + { int32_t i; + for(i=0;icosmetic_props_count;i++) { + if(!render_cosmetic_prop_save_message_body(w,&value->cosmetic_props[i])) return 0; + } } + } + } + { /* lasers */ + if(value->lasers_count<0 || value->lasers_count>32000) return 0; + if(value->lasers_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,39,kTableMessageRefBitsHere); + if(value->lasers_count<0 || value->lasers_count>32000) return 0; + table_bit_put(w,(uint64_t)value->lasers_count-0,15); + { int32_t i; + for(i=0;ilasers_count;i++) { + if(!render_laser_save_message_body(w,&value->lasers[i])) return 0; + } } + } + } + { /* explosions */ + if(value->explosions_count<0 || value->explosions_count>32000) return 0; + if(value->explosions_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,40,kTableMessageRefBitsHere); + if(value->explosions_count<0 || value->explosions_count>32000) return 0; + table_bit_put(w,(uint64_t)value->explosions_count-0,15); + { int32_t i; + for(i=0;iexplosions_count;i++) { + if(!render_explosion_save_message_body(w,&value->explosions[i])) return 0; + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_vector3_save_body( TableWriter * w, const RenderVector3 * value ) +static SCHEMA_UNUSED int64_t render_frame_measure_messages(const RenderFrame * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xbb62c62c9808ea37): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->version=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x0f9222d2ba7aa2a7): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>1?1:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;icameras[i] : &scratch; + if(!render_camera_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->cameras_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x294a5c4913e1ad44): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4096?4096:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iships[i] : &scratch; + if(!render_ship_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->ships_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x84f8260bc283608c): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>1024?1024:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iturrets[i] : &scratch; + if(!render_turret_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->turrets_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x1c3027194b1ba4d0): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4096?4096:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;imissiles[i] : &scratch; + if(!render_missile_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->missiles_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x43125398a9903d27): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4096?4096:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;idynamic_props[i] : &scratch; + if(!render_dynamic_prop_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->dynamic_props_count=(int32_t)kept; + } + break; + } + case UINT64_C(0xc1f8d7edff7fcfd2): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>20000?20000:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;istatic_props[i] : &scratch; + if(!render_static_prop_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->static_props_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x33408e39f5f480ff): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>8192?8192:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;icosmetic_props[i] : &scratch; + if(!render_cosmetic_prop_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->cosmetic_props_count=(int32_t)kept; + } + break; + } + case UINT64_C(0xd982b77b3e92d16d): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>32000?32000:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ilasers[i] : &scratch; + if(!render_laser_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->lasers_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x876a8c1a85806a69): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>32000?32000:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iexplosions[i] : &scratch; + if(!render_explosion_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->explosions_count=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_frame_load_messages(RenderFrame * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_vector3_save_body( TableWriter * w, const RenderVector3 * value ) { - if ( value->x != 0.0 ) - { - table_writer_put16( w, 0xad8b ); table_writer_put8( w, 11 ); /* x */ - table_writer_put64( w, table_double_to_bits( value->x ) ); + (void) value; + { /* x */ + if ( !( value->x == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63f54c86021707ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->x ) ); + } } - if ( value->y != 0.0 ) - { - table_writer_put16( w, 0xb2f8 ); table_writer_put8( w, 11 ); /* y */ - table_writer_put64( w, table_double_to_bits( value->y ) ); + { /* y */ + if ( !( value->y == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63f44c86021554ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->y ) ); + } } - if ( value->z != 0.0 ) - { - table_writer_put16( w, 0xaca1 ); table_writer_put8( w, 11 ); /* z */ - table_writer_put64( w, table_double_to_bits( value->z ) ); + { /* z */ + if ( !( value->z == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63f74c86021a6dull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->z ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_vector3_measure( const RenderVector3 * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_vector3_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_vector3_save( const RenderVector3 * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_vector3_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_vector3_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_vector3_load_body( TableReader * r, RenderVector3 * value ) +static SCHEMA_UNUSED int render_vector3_load_body( TableReader * r, RenderVector3 * value ) { - render_vector3_reset( value ); /* prefill declared defaults in place, then overlay */ + render_vector3_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xad8b: /* x */ + case 0xaf63f54c86021707ull: /* x */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->x = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->x = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->x = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->x = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->x = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xb2f8: /* y */ + case 0xaf63f44c86021554ull: /* y */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->y = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->y = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->y = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->y = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->y = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xaca1: /* z */ + case 0xaf63f74c86021a6dull: /* z */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->z = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->z = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->z = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->z = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->z = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_vector3_load( RenderVector3 * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_vector3_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_vector3_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t render_quaternion_measure( const RenderQuaternion * value ) -{ - int64_t bytes = 2; /* terminator */ - if ( value->x != 0.0 ) { bytes += 3 + 8; } /* x */ - if ( value->y != 0.0 ) { bytes += 3 + 8; } /* y */ - if ( value->z != 0.0 ) { bytes += 3 + 8; } /* z */ - if ( value->w != 1.0 ) { bytes += 3 + 8; } /* w */ - return bytes; +static SCHEMA_UNUSED int render_vector3_save_message_body(TableBitWriter * w,const RenderVector3 * value) +{ + (void)value; + { /* x */ + if(!(value->x == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,46,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->x),64); + } + } + { /* y */ + if(!(value->y == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,47,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->y),64); + } + } + { /* z */ + if(!(value->z == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,48,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->z),64); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_quaternion_save_body( TableWriter * w, const RenderQuaternion * value ) +static SCHEMA_UNUSED int64_t render_vector3_measure_messages(const RenderVector3 * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xaf63f54c86021707): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->x=decoded; + } + break; + } + case UINT64_C(0xaf63f44c86021554): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->y=decoded; + } + break; + } + case UINT64_C(0xaf63f74c86021a6d): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->z=decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_vector3_load_messages(RenderVector3 * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int render_quaternion_save_body( TableWriter * w, const RenderQuaternion * value ) { - if ( value->x != 0.0 ) - { - table_writer_put16( w, 0xad8b ); table_writer_put8( w, 11 ); /* x */ - table_writer_put64( w, table_double_to_bits( value->x ) ); + (void) value; + { /* x */ + if ( !( value->x == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63f54c86021707ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->x ) ); + } } - if ( value->y != 0.0 ) - { - table_writer_put16( w, 0xb2f8 ); table_writer_put8( w, 11 ); /* y */ - table_writer_put64( w, table_double_to_bits( value->y ) ); + { /* y */ + if ( !( value->y == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63f44c86021554ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->y ) ); + } } - if ( value->z != 0.0 ) - { - table_writer_put16( w, 0xaca1 ); table_writer_put8( w, 11 ); /* z */ - table_writer_put64( w, table_double_to_bits( value->z ) ); + { /* z */ + if ( !( value->z == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63f74c86021a6dull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->z ) ); + } } - if ( value->w != 1.0 ) - { - table_writer_put16( w, 0xcd3a ); table_writer_put8( w, 11 ); /* w */ - table_writer_put64( w, table_double_to_bits( value->w ) ); + { /* w */ + if ( !( value->w == 1.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63ea4c86020456ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->w ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t render_quaternion_measure( const RenderQuaternion * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !render_quaternion_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t render_quaternion_save( const RenderQuaternion * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !render_quaternion_save_body( &w, value ) ) { return -1; } - return w.offset; /* == render_quaternion_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_BLOCKDEMO_TABLE_INLINE int render_quaternion_load_body( TableReader * r, RenderQuaternion * value ) +static SCHEMA_UNUSED int render_quaternion_load_body( TableReader * r, RenderQuaternion * value ) { - render_quaternion_reset( value ); /* prefill declared defaults in place, then overlay */ + render_quaternion_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xad8b: /* x */ + case 0xaf63f54c86021707ull: /* x */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->x = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->x = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->x = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->x = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->x = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xb2f8: /* y */ + case 0xaf63f44c86021554ull: /* y */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->y = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->y = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->y = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->y = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->y = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xaca1: /* z */ + case 0xaf63f74c86021a6dull: /* z */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->z = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->z = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->z = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->z = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->z = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xcd3a: /* w */ + case 0xaf63ea4c86020456ull: /* w */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->w = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->w = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->w = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->w = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->w = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int render_quaternion_load( RenderQuaternion * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + render_quaternion_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return render_quaternion_load_body( &r, value ); } +static SCHEMA_UNUSED int render_quaternion_save_message_body(TableBitWriter * w,const RenderQuaternion * value) +{ + (void)value; + { /* x */ + if(!(value->x == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,46,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->x),64); + } + } + { /* y */ + if(!(value->y == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,47,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->y),64); + } + } + { /* z */ + if(!(value->z == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,48,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->z),64); + } + } + { /* w */ + if(!(value->w == 1.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,49,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->w),64); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t render_quaternion_measure_messages(const RenderQuaternion * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xaf63f54c86021707): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->x=decoded; + } + break; + } + case UINT64_C(0xaf63f44c86021554): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->y=decoded; + } + break; + } + case UINT64_C(0xaf63f74c86021a6d): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->z=decoded; + } + break; + } + case UINT64_C(0xaf63ea4c86020456): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->w=decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int render_quaternion_load_messages(RenderQuaternion * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_camera_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_camera_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_camera_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_camera_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_camera_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_ship_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_ship_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_ship_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_ship_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_ship_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_turret_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_turret_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_turret_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_turret_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_turret_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_missile_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_missile_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_missile_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_missile_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_missile_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_dynamic_prop_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_dynamic_prop_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_dynamic_prop_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_dynamic_prop_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_dynamic_prop_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_static_prop_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_static_prop_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_static_prop_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_static_prop_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_static_prop_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_cosmetic_prop_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_cosmetic_prop_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_cosmetic_prop_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_cosmetic_prop_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_cosmetic_prop_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_laser_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_laser_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_laser_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_laser_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_laser_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_explosion_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_explosion_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_explosion_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_explosion_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_explosion_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_frame_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_frame_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_frame_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_frame_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_frame_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_vector3_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_vector3_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_vector3_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_vector3_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_vector3_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_quaternion_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_quaternion_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_quaternion_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define render_quaternion_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define render_quaternion_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* render_camera_open: match the header and POINT. On a match the bytes ARE what this @@ -4527,6 +10884,8 @@ static SCHEMA_UNUSED int render_quaternion_load( RenderQuaternion * value, const file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderCamera * render_camera_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderCamera *)table_cook_open_ex(bytes,length,sizeof(RenderCamera),SCHEMA_TABLE_ALIGNOF(RenderCamera),reason); } static SCHEMA_UNUSED const RenderCamera * render_camera_open( const void * bytes, uint64_t length ) { return (const RenderCamera *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderCamera ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderCamera ) ); @@ -4551,6 +10910,8 @@ static SCHEMA_UNUSED const RenderCamera * render_camera_open( const void * bytes file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderShip * render_ship_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderShip *)table_cook_open_ex(bytes,length,sizeof(RenderShip),SCHEMA_TABLE_ALIGNOF(RenderShip),reason); } static SCHEMA_UNUSED const RenderShip * render_ship_open( const void * bytes, uint64_t length ) { return (const RenderShip *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderShip ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderShip ) ); @@ -4575,6 +10936,8 @@ static SCHEMA_UNUSED const RenderShip * render_ship_open( const void * bytes, ui file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderTurret * render_turret_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderTurret *)table_cook_open_ex(bytes,length,sizeof(RenderTurret),SCHEMA_TABLE_ALIGNOF(RenderTurret),reason); } static SCHEMA_UNUSED const RenderTurret * render_turret_open( const void * bytes, uint64_t length ) { return (const RenderTurret *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderTurret ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderTurret ) ); @@ -4599,6 +10962,8 @@ static SCHEMA_UNUSED const RenderTurret * render_turret_open( const void * bytes file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderMissile * render_missile_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderMissile *)table_cook_open_ex(bytes,length,sizeof(RenderMissile),SCHEMA_TABLE_ALIGNOF(RenderMissile),reason); } static SCHEMA_UNUSED const RenderMissile * render_missile_open( const void * bytes, uint64_t length ) { return (const RenderMissile *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderMissile ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderMissile ) ); @@ -4623,6 +10988,8 @@ static SCHEMA_UNUSED const RenderMissile * render_missile_open( const void * byt file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderDynamicProp * render_dynamic_prop_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderDynamicProp *)table_cook_open_ex(bytes,length,sizeof(RenderDynamicProp),SCHEMA_TABLE_ALIGNOF(RenderDynamicProp),reason); } static SCHEMA_UNUSED const RenderDynamicProp * render_dynamic_prop_open( const void * bytes, uint64_t length ) { return (const RenderDynamicProp *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderDynamicProp ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderDynamicProp ) ); @@ -4647,6 +11014,8 @@ static SCHEMA_UNUSED const RenderDynamicProp * render_dynamic_prop_open( const v file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderStaticProp * render_static_prop_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderStaticProp *)table_cook_open_ex(bytes,length,sizeof(RenderStaticProp),SCHEMA_TABLE_ALIGNOF(RenderStaticProp),reason); } static SCHEMA_UNUSED const RenderStaticProp * render_static_prop_open( const void * bytes, uint64_t length ) { return (const RenderStaticProp *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderStaticProp ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderStaticProp ) ); @@ -4671,6 +11040,8 @@ static SCHEMA_UNUSED const RenderStaticProp * render_static_prop_open( const voi file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderCosmeticProp * render_cosmetic_prop_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderCosmeticProp *)table_cook_open_ex(bytes,length,sizeof(RenderCosmeticProp),SCHEMA_TABLE_ALIGNOF(RenderCosmeticProp),reason); } static SCHEMA_UNUSED const RenderCosmeticProp * render_cosmetic_prop_open( const void * bytes, uint64_t length ) { return (const RenderCosmeticProp *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderCosmeticProp ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderCosmeticProp ) ); @@ -4695,6 +11066,8 @@ static SCHEMA_UNUSED const RenderCosmeticProp * render_cosmetic_prop_open( const file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderLaser * render_laser_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderLaser *)table_cook_open_ex(bytes,length,sizeof(RenderLaser),SCHEMA_TABLE_ALIGNOF(RenderLaser),reason); } static SCHEMA_UNUSED const RenderLaser * render_laser_open( const void * bytes, uint64_t length ) { return (const RenderLaser *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderLaser ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderLaser ) ); @@ -4719,6 +11092,8 @@ static SCHEMA_UNUSED const RenderLaser * render_laser_open( const void * bytes, file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderExplosion * render_explosion_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderExplosion *)table_cook_open_ex(bytes,length,sizeof(RenderExplosion),SCHEMA_TABLE_ALIGNOF(RenderExplosion),reason); } static SCHEMA_UNUSED const RenderExplosion * render_explosion_open( const void * bytes, uint64_t length ) { return (const RenderExplosion *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderExplosion ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderExplosion ) ); @@ -4743,11 +11118,297 @@ static SCHEMA_UNUSED const RenderExplosion * render_explosion_open( const void * file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RenderFrame * render_frame_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RenderFrame *)table_cook_open_ex(bytes,length,sizeof(RenderFrame),SCHEMA_TABLE_ALIGNOF(RenderFrame),reason); } static SCHEMA_UNUSED const RenderFrame * render_frame_open( const void * bytes, uint64_t length ) { return (const RenderFrame *) table_cook_open( bytes, length, (uint64_t) sizeof( RenderFrame ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RenderFrame ) ); } +static SCHEMA_UNUSED int schema_blockdemo_render_camera_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderCamera * value=(const RenderCamera *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_vector3_cook_body_(n,at+0,&value->position,order)) { return 0; } + if(!schema_blockdemo_render_quaternion_cook_body_(n,at+24,&value->rotation,order)) { return 0; } + table_cook_put(at+56,(uint64_t)value->camera_id,4,order); + table_cook_put(at+60,(uint64_t)value->camera_type,4,order); + table_cook_put(at+64,(uint64_t)value->target_object_id,4,order); + { uint32_t bits; memcpy(&bits,&value->fov,4); table_cook_put(at+68,bits,4,order); } + return 1; +} +static SCHEMA_UNUSED int64_t render_camera_cook_measure(const RenderCamera * value) { (void)value; return 152; } +static SCHEMA_UNUSED int render_camera_cook(const RenderCamera * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<152) { return 0; } + memset(raw,0,152); + if(!schema_blockdemo_render_camera_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+144,UINT64_C(0x11c6b29d15f30306),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,72,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_ship_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderShip * value=(const RenderShip *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_vector3_cook_body_(n,at+0,&value->position,order)) { return 0; } + if(!schema_blockdemo_render_quaternion_cook_body_(n,at+24,&value->rotation,order)) { return 0; } + table_cook_put(at+56,(uint64_t)value->flags,8,order); + table_cook_put(at+64,(uint64_t)value->object_id,4,order); + table_cook_put(at+68,(uint64_t)value->target_object_id,4,order); + { uint32_t bits; memcpy(&bits,&value->thrust,4); table_cook_put(at+72,bits,4,order); } + table_cook_put(at+76,(uint64_t)value->object_sequence,1,order); + table_cook_put(at+77,(uint64_t)value->ship_type,1,order); + table_cook_put(at+78,(uint64_t)value->team,1,order); + table_cook_put(at+79,value->has_target_lock ? 1 : 0,1,order); + table_cook_put(at+80,value->predicted_explode ? 1 : 0,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_ship_cook_measure(const RenderShip * value) { (void)value; return 168; } +static SCHEMA_UNUSED int render_ship_cook(const RenderShip * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<168) { return 0; } + memset(raw,0,168); + if(!schema_blockdemo_render_ship_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+160,UINT64_C(0x5b00edd93fad6007),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,88,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_turret_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderTurret * value=(const RenderTurret *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_quaternion_cook_body_(n,at+0,&value->rotation,order)) { return 0; } + table_cook_put(at+32,(uint64_t)value->flags,8,order); + table_cook_put(at+40,(uint64_t)value->object_id,4,order); + table_cook_put(at+44,(uint64_t)value->parent_object_id,4,order); + table_cook_put(at+48,(uint64_t)value->turret_index,4,order); + table_cook_put(at+52,(uint64_t)value->target_object_id,4,order); + table_cook_put(at+56,(uint64_t)value->object_sequence,1,order); + table_cook_put(at+57,(uint64_t)value->team,1,order); + table_cook_put(at+58,value->has_target_lock ? 1 : 0,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_turret_cook_measure(const RenderTurret * value) { (void)value; return 144; } +static SCHEMA_UNUSED int render_turret_cook(const RenderTurret * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<144) { return 0; } + memset(raw,0,144); + if(!schema_blockdemo_render_turret_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+136,UINT64_C(0x4034181fa408c29f),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,64,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_missile_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderMissile * value=(const RenderMissile *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_vector3_cook_body_(n,at+0,&value->position,order)) { return 0; } + if(!schema_blockdemo_render_quaternion_cook_body_(n,at+24,&value->rotation,order)) { return 0; } + table_cook_put(at+56,(uint64_t)value->flags,8,order); + table_cook_put(at+64,(uint64_t)value->object_id,4,order); + table_cook_put(at+68,(uint64_t)value->object_sequence,1,order); + table_cook_put(at+69,(uint64_t)value->missile_type,1,order); + table_cook_put(at+70,(uint64_t)value->team,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_missile_cook_measure(const RenderMissile * value) { (void)value; return 152; } +static SCHEMA_UNUSED int render_missile_cook(const RenderMissile * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<152) { return 0; } + memset(raw,0,152); + if(!schema_blockdemo_render_missile_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+144,UINT64_C(0x93dab31114da135b),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,72,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_dynamic_prop_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderDynamicProp * value=(const RenderDynamicProp *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_vector3_cook_body_(n,at+0,&value->position,order)) { return 0; } + if(!schema_blockdemo_render_quaternion_cook_body_(n,at+24,&value->rotation,order)) { return 0; } + table_cook_put(at+56,(uint64_t)value->flags,8,order); + table_cook_put(at+64,(uint64_t)value->object_id,4,order); + table_cook_put(at+68,(uint64_t)value->object_sequence,1,order); + table_cook_put(at+69,(uint64_t)value->prop_type,1,order); + table_cook_put(at+70,(uint64_t)value->team,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_dynamic_prop_cook_measure(const RenderDynamicProp * value) { (void)value; return 152; } +static SCHEMA_UNUSED int render_dynamic_prop_cook(const RenderDynamicProp * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<152) { return 0; } + memset(raw,0,152); + if(!schema_blockdemo_render_dynamic_prop_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+144,UINT64_C(0xcca6d00e1bb40147),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,72,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_static_prop_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderStaticProp * value=(const RenderStaticProp *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_vector3_cook_body_(n,at+0,&value->position,order)) { return 0; } + if(!schema_blockdemo_render_quaternion_cook_body_(n,at+24,&value->rotation,order)) { return 0; } + { uint64_t bits; memcpy(&bits,&value->scale,8); table_cook_put(at+56,bits,8,order); } + table_cook_put(at+64,(uint64_t)value->flags,8,order); + table_cook_put(at+72,(uint64_t)value->static_prop_id,4,order); + table_cook_put(at+76,(uint64_t)value->prop_type,1,order); + table_cook_put(at+77,(uint64_t)value->team,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_static_prop_cook_measure(const RenderStaticProp * value) { (void)value; return 160; } +static SCHEMA_UNUSED int render_static_prop_cook(const RenderStaticProp * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<160) { return 0; } + memset(raw,0,160); + if(!schema_blockdemo_render_static_prop_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+152,UINT64_C(0xc19ad62b865276fe),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,80,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_cosmetic_prop_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderCosmeticProp * value=(const RenderCosmeticProp *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_vector3_cook_body_(n,at+0,&value->position,order)) { return 0; } + if(!schema_blockdemo_render_quaternion_cook_body_(n,at+24,&value->rotation,order)) { return 0; } + { uint64_t bits; memcpy(&bits,&value->scale,8); table_cook_put(at+56,bits,8,order); } + table_cook_put(at+64,(uint64_t)value->flags,8,order); + table_cook_put(at+72,(uint64_t)value->cosmetic_prop_id,4,order); + table_cook_put(at+76,(uint64_t)value->prop_sequence,1,order); + table_cook_put(at+77,(uint64_t)value->prop_type,1,order); + table_cook_put(at+78,(uint64_t)value->team,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_cosmetic_prop_cook_measure(const RenderCosmeticProp * value) { (void)value; return 160; } +static SCHEMA_UNUSED int render_cosmetic_prop_cook(const RenderCosmeticProp * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<160) { return 0; } + memset(raw,0,160); + if(!schema_blockdemo_render_cosmetic_prop_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+152,UINT64_C(0x5ff5bdbae776636f),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,80,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_laser_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderLaser * value=(const RenderLaser *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_vector3_cook_body_(n,at+0,&value->start,order)) { return 0; } + if(!schema_blockdemo_render_vector3_cook_body_(n,at+24,&value->finish,order)) { return 0; } + { uint64_t bits; memcpy(&bits,&value->t,8); table_cook_put(at+48,bits,8,order); } + table_cook_put(at+56,(uint64_t)value->laser_id,4,order); + table_cook_put(at+60,(uint64_t)value->laser_type,1,order); + table_cook_put(at+61,(uint64_t)value->team,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_laser_cook_measure(const RenderLaser * value) { (void)value; return 144; } +static SCHEMA_UNUSED int render_laser_cook(const RenderLaser * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<144) { return 0; } + memset(raw,0,144); + if(!schema_blockdemo_render_laser_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+136,UINT64_C(0xb0947dbe6c23d994),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,64,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_explosion_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderExplosion * value=(const RenderExplosion *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_blockdemo_render_vector3_cook_body_(n,at+0,&value->position,order)) { return 0; } + if(!schema_blockdemo_render_quaternion_cook_body_(n,at+24,&value->rotation,order)) { return 0; } + { uint64_t bits; memcpy(&bits,&value->t,8); table_cook_put(at+56,bits,8,order); } + table_cook_put(at+64,(uint64_t)value->explosion_id,4,order); + table_cook_put(at+68,(uint64_t)value->parent_object_id,4,order); + table_cook_put(at+72,(uint64_t)value->explosion_type,1,order); + table_cook_put(at+73,(uint64_t)value->team,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_explosion_cook_measure(const RenderExplosion * value) { (void)value; return 160; } +static SCHEMA_UNUSED int render_explosion_cook(const RenderExplosion * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<160) { return 0; } + memset(raw,0,160); + if(!schema_blockdemo_render_explosion_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+152,UINT64_C(0x0cc738b585c9e5f8),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,80,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_frame_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderFrame * value=(const RenderFrame *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->version,8,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<1;cook_i1++) { + if(!schema_blockdemo_render_camera_cook_body_(n,at+8+cook_i1*72,&value->cameras[cook_i1],order)) { return 0; } + } } + table_cook_put(at+8+72,(uint32_t)value->cameras_count,4,order); + { int32_t cook_i2; for(cook_i2=0;cook_i2<4096;cook_i2++) { + if(!schema_blockdemo_render_ship_cook_body_(n,at+88+cook_i2*88,&value->ships[cook_i2],order)) { return 0; } + } } + table_cook_put(at+88+360448,(uint32_t)value->ships_count,4,order); + { int32_t cook_i3; for(cook_i3=0;cook_i3<1024;cook_i3++) { + if(!schema_blockdemo_render_turret_cook_body_(n,at+360544+cook_i3*64,&value->turrets[cook_i3],order)) { return 0; } + } } + table_cook_put(at+360544+65536,(uint32_t)value->turrets_count,4,order); + { int32_t cook_i4; for(cook_i4=0;cook_i4<4096;cook_i4++) { + if(!schema_blockdemo_render_missile_cook_body_(n,at+426088+cook_i4*72,&value->missiles[cook_i4],order)) { return 0; } + } } + table_cook_put(at+426088+294912,(uint32_t)value->missiles_count,4,order); + { int32_t cook_i5; for(cook_i5=0;cook_i5<4096;cook_i5++) { + if(!schema_blockdemo_render_dynamic_prop_cook_body_(n,at+721008+cook_i5*72,&value->dynamic_props[cook_i5],order)) { return 0; } + } } + table_cook_put(at+721008+294912,(uint32_t)value->dynamic_props_count,4,order); + { int32_t cook_i6; for(cook_i6=0;cook_i6<20000;cook_i6++) { + if(!schema_blockdemo_render_static_prop_cook_body_(n,at+1015928+cook_i6*80,&value->static_props[cook_i6],order)) { return 0; } + } } + table_cook_put(at+1015928+1600000,(uint32_t)value->static_props_count,4,order); + { int32_t cook_i7; for(cook_i7=0;cook_i7<8192;cook_i7++) { + if(!schema_blockdemo_render_cosmetic_prop_cook_body_(n,at+2615936+cook_i7*80,&value->cosmetic_props[cook_i7],order)) { return 0; } + } } + table_cook_put(at+2615936+655360,(uint32_t)value->cosmetic_props_count,4,order); + { int32_t cook_i8; for(cook_i8=0;cook_i8<32000;cook_i8++) { + if(!schema_blockdemo_render_laser_cook_body_(n,at+3271304+cook_i8*64,&value->lasers[cook_i8],order)) { return 0; } + } } + table_cook_put(at+3271304+2048000,(uint32_t)value->lasers_count,4,order); + { int32_t cook_i9; for(cook_i9=0;cook_i9<32000;cook_i9++) { + if(!schema_blockdemo_render_explosion_cook_body_(n,at+5319312+cook_i9*80,&value->explosions[cook_i9],order)) { return 0; } + } } + table_cook_put(at+5319312+2560000,(uint32_t)value->explosions_count,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t render_frame_cook_measure(const RenderFrame * value) { (void)value; return 7879400; } +static SCHEMA_UNUSED int render_frame_cook(const RenderFrame * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<7879400) { return 0; } + memset(raw,0,7879400); + if(!schema_blockdemo_render_frame_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+7879392,UINT64_C(0x9b2749fb3fc30f4e),8,order); + table_cook_header(raw,SCHEMA_BLOCKDEMO_BUILD_VERSION_VALUE,7879320,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_vector3_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderVector3 * value=(const RenderVector3 *)storage; + (void)n; (void)at; (void)value; (void)order; + { uint64_t bits; memcpy(&bits,&value->x,8); table_cook_put(at+0,bits,8,order); } + { uint64_t bits; memcpy(&bits,&value->y,8); table_cook_put(at+8,bits,8,order); } + { uint64_t bits; memcpy(&bits,&value->z,8); table_cook_put(at+16,bits,8,order); } + return 1; +} +static SCHEMA_UNUSED int schema_blockdemo_render_quaternion_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RenderQuaternion * value=(const RenderQuaternion *)storage; + (void)n; (void)at; (void)value; (void)order; + { uint64_t bits; memcpy(&bits,&value->x,8); table_cook_put(at+0,bits,8,order); } + { uint64_t bits; memcpy(&bits,&value->y,8); table_cook_put(at+8,bits,8,order); } + { uint64_t bits; memcpy(&bits,&value->z,8); table_cook_put(at+16,bits,8,order); } + { uint64_t bits; memcpy(&bits,&value->w,8); table_cook_put(at+24,bits,8,order); } + return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/examples-c/GuardedTable.c b/testdata/golden/tables/examples-c/GuardedTable.c index 475c24799..c868b7121 100644 --- a/testdata/golden/tables/examples-c/GuardedTable.c +++ b/testdata/golden/tables/examples-c/GuardedTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) - { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,12 +2330,12 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableFieldInfo schema_tabledemo_patrol_fields_[] = { - { "active", "active", "bool", 0x405a, 1, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, active ), (uint32_t) sizeof( ( (Patrol *) 0 )->active ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "speed", "speed", "float32", 0xbc00, 10, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, speed ), (uint32_t) sizeof( ( (Patrol *) 0 )->speed ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "active", TableDocNone, 0, NULL }, - { "has_target", "has_target", "bool", 0xb0a7, 1, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, has_target ), (uint32_t) sizeof( ( (Patrol *) 0 )->has_target ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "active", TableDocNone, 0, NULL }, - { "target_id", "target_id", "int32", 0xdf6a, 4, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, target_id ), (uint32_t) sizeof( ( (Patrol *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, -1, NULL, 0, NULL, NULL, -1, NULL, "active && has_target", TableDocNone, 0, NULL }, - { "wander", "wander", "float32", 0x832f, 10, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, wander ), (uint32_t) sizeof( ( (Patrol *) 0 )->wander ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "active && !has_target", TableDocNone, 0, NULL }, - { "note", "note", "string", 0x9da7, 12, 0, 1, 0, 8, (uint32_t) offsetof( Patrol, note ), (uint32_t) sizeof( ( (Patrol *) 0 )->note ), (uint32_t) offsetof( Patrol, note_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "!active", TableDocNone, 0, NULL }, + { "active", "active", "bool", 0x6580790b036f0c6full, 1, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, active ), (uint32_t) sizeof( ( (Patrol *) 0 )->active ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "speed", "speed", "float32", 0x2281498aa0200e40ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, speed ), (uint32_t) sizeof( ( (Patrol *) 0 )->speed ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "active", TableDocNone, 0, NULL, 0, NULL }, + { "has_target", "has_target", "bool", 0x247f0e7f55aacfbdull, 1, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, has_target ), (uint32_t) sizeof( ( (Patrol *) 0 )->has_target ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "active", TableDocNone, 0, NULL, 0, NULL }, + { "target_id", "target_id", "int32", 0xb7bc9ac015a25050ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, target_id ), (uint32_t) sizeof( ( (Patrol *) 0 )->target_id ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "active && has_target", TableDocNone, 0, NULL, 0, NULL }, + { "wander", "wander", "float32", 0xb8c758d8bd1845d4ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Patrol, wander ), (uint32_t) sizeof( ( (Patrol *) 0 )->wander ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "active && !has_target", TableDocNone, 0, NULL, 0, NULL }, + { "note", "note", "string", 0x3bf8fbbad1587cddull, 12, 0, 1, 0, 8, (uint32_t) offsetof( Patrol, note ), (uint32_t) sizeof( ( (Patrol *) 0 )->note ), (uint32_t) offsetof( Patrol, note_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "!active", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_patrol_info_ = { "Patrol", (uint32_t) sizeof( Patrol ), 6, schema_tabledemo_patrol_fields_, schema_tabledemo_patrol_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/examples-c/GuardedTable.h b/testdata/golden/tables/examples-c/GuardedTable.h index c8b43c01a..ef32bfd93 100644 --- a/testdata/golden/tables/examples-c/GuardedTable.h +++ b/testdata/golden/tables/examples-c/GuardedTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package tabledemo — protocol id 0xba7b344f28584d33 (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_TABLEDEMO_GUARDEDTABLE_H #define SCHEMA_TABLEDEMO_GUARDEDTABLE_H @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -183,6 +194,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -210,6 +223,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -231,157 +246,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[156]; + uint32_t slots[512], positions[156]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (512-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(512-1); + } + if(v->count==156) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } +} +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) +{ + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; } - return 0; + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; } +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; +} + +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -391,6 +526,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_TABLEDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_TABLEDEMO_BUILD_VERSION #define SCHEMA_TABLEDEMO_BUILD_VERSION @@ -502,25 +692,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -530,40 +720,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_TABLEDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table Patrol — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in patrol_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -613,109 +1384,133 @@ static SCHEMA_UNUSED int64_t patrol_measure( const Patrol * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_save_body( TableWriter * w, const Patrol * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_load_body( TableReader * r, Patrol * value ); -static SCHEMA_UNUSED int64_t patrol_measure( const Patrol * value ) +static SCHEMA_UNUSED int schema_tabledemo_patrol_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int patrol_save_message_body(TableBitWriter *,const Patrol *); +static SCHEMA_UNUSED int patrol_load_message_body(TableMessageReader *,Patrol *); +static SCHEMA_UNUSED int schema_tabledemo_patrol_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_patrol_wire_note_( TableWriter * w, const Patrol * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->active != 0 ) { bytes += 3 + 1; } /* active */ - if ( value->active ) - { - if ( value->speed != 1.0f ) { bytes += 3 + 4; } /* speed */ - } - if ( value->active ) - { - if ( value->has_target != 0 ) { bytes += 3 + 1; } /* has_target */ - } - if ( value->active && value->has_target ) - { - if ( value->target_id != 0 ) { bytes += 3 + 4; } /* target_id */ - } - if ( value->active && !value->has_target ) - { - if ( value->wander != 0.5f ) { bytes += 3 + 4; } /* wander */ - } - if ( !value->active ) - { - if ( value->note_length < 0 || value->note_length > 8 ) { return -1; } /* storage invariant */ - if ( value->note_length > 0 ) { bytes += 3 + 4 + value->note_length; } /* note */ - } - return bytes; + if ( value->note_length < 0 || value->note_length > 8 ) { return 0; } + table_writer_raw( w, value->note, value->note_length ); + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_save_body( TableWriter * w, const Patrol * value ) +static SCHEMA_UNUSED int patrol_save_body( TableWriter * w, const Patrol * value ) { - if ( value->active != 0 ) - { - table_writer_put16( w, 0x405a ); table_writer_put8( w, 1 ); /* active */ - table_writer_put8( w, value->active ? 1 : 0 ); + (void) value; + { /* active */ + if ( !( value->active == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6580790b036f0c6full ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->active ? 1 : 0 ); + } } + { /* speed */ if ( value->active ) { - if ( value->speed != 1.0f ) + if ( !( value->speed == 1.0f ) ) { - table_writer_put16( w, 0xbc00 ); table_writer_put8( w, 10 ); /* speed */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x2281498aa0200e40ull ); table_writer_put8( w, 10 ); table_writer_put32( w, table_float_to_bits( value->speed ) ); } } + } + { /* has_target */ if ( value->active ) { - if ( value->has_target != 0 ) + if ( !( value->has_target == 0 ) ) { - table_writer_put16( w, 0xb0a7 ); table_writer_put8( w, 1 ); /* has_target */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x247f0e7f55aacfbdull ); table_writer_put8( w, 1 ); table_writer_put8( w, value->has_target ? 1 : 0 ); } } + } + { /* target_id */ if ( value->active && value->has_target ) { - if ( value->target_id != 0 ) + if ( !( value->target_id == 0 ) ) { - table_writer_put16( w, 0xdf6a ); table_writer_put8( w, 4 ); /* target_id */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb7bc9ac015a25050ull ); table_writer_put8( w, 4 ); table_writer_put32( w, (uint32_t) ( value->target_id ) ); } } + } + { /* wander */ if ( value->active && !value->has_target ) { - if ( value->wander != 0.5f ) + if ( !( value->wander == 0.5f ) ) { - table_writer_put16( w, 0x832f ); table_writer_put8( w, 10 ); /* wander */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb8c758d8bd1845d4ull ); table_writer_put8( w, 10 ); table_writer_put32( w, table_float_to_bits( value->wander ) ); } } + } + { /* note */ if ( !value->active ) { - if ( value->note_length < 0 || value->note_length > 8 ) { return 0; } /* storage invariant */ - if ( value->note_length > 0 ) + if ( value->note_length < 0 || value->note_length > 8 ) { return 0; } + if ( value->note_length != 0 ) { - table_writer_put16( w, 0x9da7 ); table_writer_put8( w, 12 ); /* note */ - table_writer_put32( w, (uint32_t) value->note_length ); - table_writer_raw( w, value->note, value->note_length ); + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x3bf8fbbad1587cddull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_patrol_wire_note_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_patrol_wire_note_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_patrol_wire_note_( w, value ) ) { return 0; } + } } } - table_writer_put16( w, 0 ); /* terminator */ + } + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t patrol_measure( const Patrol * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !patrol_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t patrol_save( const Patrol * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !patrol_save_body( &w, value ) ) { return -1; } - return w.offset; /* == patrol_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_load_body( TableReader * r, Patrol * value ) +static SCHEMA_UNUSED int patrol_load_body( TableReader * r, Patrol * value ) { - patrol_reset( value ); /* prefill declared defaults in place, then overlay */ + patrol_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x405a: /* active */ + case 0x6580790b036f0c6full: /* active */ { if ( kind != 1 ) { @@ -723,11 +1518,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_load_body( TableRe if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->active = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->active = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xbc00: /* speed */ + case 0x2281498aa0200e40ull: /* speed */ { if ( kind != 10 ) { @@ -735,11 +1538,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_load_body( TableRe if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->speed = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->speed = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xb0a7: /* has_target */ + case 0x247f0e7f55aacfbdull: /* has_target */ { if ( kind != 1 ) { @@ -747,28 +1558,100 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_load_body( TableRe if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->has_target = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->has_target = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xdf6a: /* target_id */ + case 0xb7bc9ac015a25050ull: /* target_id */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->target_id = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->target_id = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->target_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } - value->target_id = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->target_id = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->target_id = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->target_id = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x832f: /* wander */ + case 0xb8c758d8bd1845d4ull: /* wander */ { if ( kind != 10 ) { @@ -776,11 +1659,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_load_body( TableRe if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->wander = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->wander = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x9da7: /* note */ + case 0x3bf8fbbad1587cddull: /* note */ { if ( kind != 12 ) { @@ -788,38 +1679,298 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int patrol_load_body( TableRe if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 8 ) { keep = 8; r->report->clamped++; } - memcpy( value->note, r->buffer + r->offset, keep ); - value->note[keep] = 0; - value->note_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->note[0] = 0; value->note_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 8 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 8 ); r->report->clamped++; } + memcpy( value->note, sub.buffer, (size_t) keep ); value->note[keep] = 0; value->note_length = (int32_t) keep; break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int patrol_load( Patrol * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + patrol_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return patrol_load_body( &r, value ); } +static SCHEMA_UNUSED int patrol_save_message_body(TableBitWriter * w,const Patrol * value) +{ + (void)value; + { /* active */ + if(!(value->active == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,32,kTableMessageRefBitsHere); + table_bit_put(w,value->active ? 1 : 0,1); + } + } + { /* speed */ + if(value->active) { + if(!(value->speed == 1.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,33,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->speed),32); + } + } + } + { /* has_target */ + if(value->active) { + if(!(value->has_target == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,34,kTableMessageRefBitsHere); + table_bit_put(w,value->has_target ? 1 : 0,1); + } + } + } + { /* target_id */ + if(value->active && value->has_target) { + if(!(value->target_id == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,35,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->target_id)-UINT64_C(0),10); + } + } + } + { /* wander */ + if(value->active && !value->has_target) { + if(!(value->wander == 0.5f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,36,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->wander),32); + } + } + } + { /* note */ + if(!value->active) { + if(value->note_length<0 || value->note_length>8) return 0; + if(value->note_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,37,kTableMessageRefBitsHere); + if(value->note_length<0 || value->note_length>8) return 0; + table_bit_put(w,(uint64_t)value->note_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->note,value->note_length); + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t patrol_measure_messages(const Patrol * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x6580790b036f0c6f): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->active=lo!=0; + } + break; + } + case UINT64_C(0x2281498aa0200e40): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->speed=decoded; + } + break; + } + case UINT64_C(0x247f0e7f55aacfbd): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->has_target=lo!=0; + } + break; + } + case UINT64_C(0xb7bc9ac015a25050): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>1000ll){decoded=1000ll; + r->report->clamped++; + } + value->target_id=(int32_t)decoded; + } + break; + } + case UINT64_C(0xb8c758d8bd1845d4): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->wander=decoded; + } + break; + } + case UINT64_C(0x3bf8fbbad1587cdd): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,8); + memcpy(value->note,text,(size_t)kept); + value->note[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->note_length=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int patrol_load_messages(Patrol * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define patrol_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define patrol_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define patrol_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define patrol_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define patrol_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* patrol_open: match the header and POINT. On a match the bytes ARE what this @@ -841,11 +1992,36 @@ static SCHEMA_UNUSED int patrol_load( Patrol * value, const uint8_t * buffer, in file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const Patrol * patrol_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const Patrol *)table_cook_open_ex(bytes,length,sizeof(Patrol),SCHEMA_TABLE_ALIGNOF(Patrol),reason); } static SCHEMA_UNUSED const Patrol * patrol_open( const void * bytes, uint64_t length ) { return (const Patrol *) table_cook_open( bytes, length, (uint64_t) sizeof( Patrol ), (uint64_t) SCHEMA_TABLE_ALIGNOF( Patrol ) ); } +static SCHEMA_UNUSED int schema_tabledemo_patrol_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Patrol * value=(const Patrol *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,value->active ? 1 : 0,1,order); + { uint32_t bits; memcpy(&bits,&value->speed,4); table_cook_put(at+4,bits,4,order); } + table_cook_put(at+8,value->has_target ? 1 : 0,1,order); + table_cook_put(at+12,(uint64_t)value->target_id,4,order); + { uint32_t bits; memcpy(&bits,&value->wander,4); table_cook_put(at+16,bits,4,order); } + table_cook_bytes(at+20,value->note,value->note_length,9); + table_cook_put(at+20+12,(uint32_t)value->note_length,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t patrol_cook_measure(const Patrol * value) { (void)value; return 120; } +static SCHEMA_UNUSED int patrol_cook(const Patrol * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<120) { return 0; } + memset(raw,0,120); + if(!schema_tabledemo_patrol_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+112,UINT64_C(0x1c182e1c2529027b),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,40,1,8,order); return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/examples-c/KeyedTable.c b/testdata/golden/tables/examples-c/KeyedTable.c index 60c755a40..39427b2c2 100644 --- a/testdata/golden/tables/examples-c/KeyedTable.c +++ b/testdata/golden/tables/examples-c/KeyedTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else - { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,69 +2330,69 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableFieldInfo schema_tabledemo_team_config_fields_[] = { - { "spawn_count", "spawn_count", "int32", 0x3668, 4, 0, 0, 0, 0, (uint32_t) offsetof( TeamConfig, spawn_count ), (uint32_t) sizeof( ( (TeamConfig *) 0 )->spawn_count ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 64.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "banner", "banner", "string", 0x80e4, 12, 0, 1, 0, 16, (uint32_t) offsetof( TeamConfig, banner ), (uint32_t) sizeof( ( (TeamConfig *) 0 )->banner ), (uint32_t) offsetof( TeamConfig, banner_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "spawn_count", "spawn_count", "int32", 0xceec99e2d65db674ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( TeamConfig, spawn_count ), (uint32_t) sizeof( ( (TeamConfig *) 0 )->spawn_count ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 64.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "banner", "banner", "string", 0xbca0dab1c7a00ccfull, 12, 0, 1, 0, 16, (uint32_t) offsetof( TeamConfig, banner ), (uint32_t) sizeof( ( (TeamConfig *) 0 )->banner ), (uint32_t) offsetof( TeamConfig, banner_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_team_config_info_ = { "TeamConfig", (uint32_t) sizeof( TeamConfig ), 2, schema_tabledemo_team_config_fields_, schema_tabledemo_team_config_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_gunner_config_fields_[] = { - { "reaction", "reaction", "float32", 0x900f, 10, 0, 0, 0, 0, (uint32_t) offsetof( GunnerConfig, reaction ), (uint32_t) sizeof( ( (GunnerConfig *) 0 )->reaction ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "tracking", "tracking", "bool", 0x53d5, 1, 0, 0, 0, 0, (uint32_t) offsetof( GunnerConfig, tracking ), (uint32_t) sizeof( ( (GunnerConfig *) 0 )->tracking ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "reaction", "reaction", "float32", 0xb75aa3662201646aull, 10, 0, 0, 0, 0, (uint32_t) offsetof( GunnerConfig, reaction ), (uint32_t) sizeof( ( (GunnerConfig *) 0 )->reaction ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tracking", "tracking", "bool", 0xa6bf719a4602b0bcull, 1, 0, 0, 0, 0, (uint32_t) offsetof( GunnerConfig, tracking ), (uint32_t) sizeof( ( (GunnerConfig *) 0 )->tracking ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_gunner_config_info_ = { "GunnerConfig", (uint32_t) sizeof( GunnerConfig ), 2, schema_tabledemo_gunner_config_fields_, schema_tabledemo_gunner_config_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_turret_config_fields_[] = { - { "damage", "damage", "float32", 0x15a9, 10, 0, 0, 0, 0, (uint32_t) offsetof( TurretConfig, damage ), (uint32_t) sizeof( ( (TurretConfig *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "cooldown", "cooldown", "float32", 0x2230, 10, 0, 0, 0, 0, (uint32_t) offsetof( TurretConfig, cooldown ), (uint32_t) sizeof( ( (TurretConfig *) 0 )->cooldown ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "gunner", "gunner", "GunnerConfig", 0x2bc9, 13, 0, 0, 1, 0, (uint32_t) offsetof( TurretConfig, gunner ), (uint32_t) sizeof( ( (TurretConfig *) 0 )->gunner ), 0xffffffffu, (uint32_t) offsetof( TurretConfig, gunner_present ), &schema_tabledemo_gunner_config_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "damage", "damage", "float32", 0x7f6308be8ab37fc0ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TurretConfig, damage ), (uint32_t) sizeof( ( (TurretConfig *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "cooldown", "cooldown", "float32", 0xdc2cbe6953343d48ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( TurretConfig, cooldown ), (uint32_t) sizeof( ( (TurretConfig *) 0 )->cooldown ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "gunner", "gunner", "GunnerConfig", 0x40dbb648c0cd44aaull, 13, 0, 0, 1, 0, (uint32_t) offsetof( TurretConfig, gunner ), (uint32_t) sizeof( ( (TurretConfig *) 0 )->gunner ), 0xffffffffu, (uint32_t) offsetof( TurretConfig, gunner_present ), &schema_tabledemo_gunner_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_turret_config_info_ = { "TurretConfig", (uint32_t) sizeof( TurretConfig ), 3, schema_tabledemo_turret_config_fields_, schema_tabledemo_turret_config_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_tabledemo_hull_config_turrets_keys_[] = { - { "None", 0x0000 }, - { "Cannon", 0xf055 }, - { "Missile", 0x16b3 }, - { "Mine", 0x61bf }, + { "None", 0x0000000000000000ull }, + { "Cannon", 0x5854debe3b2e767cull }, + { "Missile", 0xb528592e5a4583e3ull }, + { "Mine", 0x04dc16aea8ff5276ull }, }; static const TableFieldInfo schema_tabledemo_hull_config_fields_[] = { - { "health", "health", "float32", 0x8617, 10, 0, 0, 0, 0, (uint32_t) offsetof( HullConfig, health ), (uint32_t) sizeof( ( (HullConfig *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "mass", "mass", "float32", 0xe7a6, 10, 0, 0, 0, 0, (uint32_t) offsetof( HullConfig, mass ), (uint32_t) sizeof( ( (HullConfig *) 0 )->mass ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "turrets", "turrets", "TurretConfig", 0x48ad, 13, 1, 0, 0, (int32_t) WEAPON_MAX, (uint32_t) offsetof( HullConfig, turrets ), (uint32_t) sizeof( ( (HullConfig *) 0 )->turrets[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_turret_config_info_, 0, 0.0, 0.0, -1, NULL, 0, "Weapon", schema_tabledemo_hull_config_turrets_keys_, 3, NULL, "", TableDocNone, 0, NULL }, + { "health", "health", "float32", 0x7f69d4b5288ba9cfull, 10, 0, 0, 0, 0, (uint32_t) offsetof( HullConfig, health ), (uint32_t) sizeof( ( (HullConfig *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "mass", "mass", "float32", 0x1f3757a2ce7b0ab1ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( HullConfig, mass ), (uint32_t) sizeof( ( (HullConfig *) 0 )->mass ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "turrets", "turrets", "TurretConfig", 0x84f8260bc283608cull, 13, 1, 0, 0, (int32_t) WEAPON_MAX, (uint32_t) offsetof( HullConfig, turrets ), (uint32_t) sizeof( ( (HullConfig *) 0 )->turrets[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_turret_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, "Weapon", schema_tabledemo_hull_config_turrets_keys_, 3, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_hull_config_info_ = { "HullConfig", (uint32_t) sizeof( HullConfig ), 3, schema_tabledemo_hull_config_fields_, schema_tabledemo_hull_config_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_tabledemo_keyed_config_teams_keys_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, }; static const TableVariantInfo schema_tabledemo_keyed_config_hulls_keys_[] = { - { "None", 0x0000 }, - { "Interceptor", 0xf0f0 }, - { "Gunship", 0xb534 }, - { "Freighter", 0xb617 }, + { "None", 0x0000000000000000ull }, + { "Interceptor", 0xae61a6cbe88c2cf4ull }, + { "Gunship", 0x334d24e1420dbc1full }, + { "Freighter", 0x6c2321a3d00e23dbull }, }; static const TableFieldInfo schema_tabledemo_keyed_config_fields_[] = { - { "teams", "teams", "TeamConfig", 0x9ae1, 13, 1, 0, 0, (int32_t) TEAM_MAX, (uint32_t) offsetof( KeyedConfig, teams ), (uint32_t) sizeof( ( (KeyedConfig *) 0 )->teams[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_team_config_info_, 0, 0.0, 0.0, -1, NULL, 0, "Team", schema_tabledemo_keyed_config_teams_keys_, 3, NULL, "", TableDocNone, 0, NULL }, - { "hulls", "hulls", "HullConfig", 0xeff5, 13, 1, 0, 0, (int32_t) HULL_MAX, (uint32_t) offsetof( KeyedConfig, hulls ), (uint32_t) sizeof( ( (KeyedConfig *) 0 )->hulls[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_hull_config_info_, 0, 0.0, 0.0, -1, NULL, 0, "Hull", schema_tabledemo_keyed_config_hulls_keys_, 3, NULL, "", TableDocNone, 0, NULL }, - { "scores", "scores", "ScoreBoard", 0xdcf3, 13, 0, 0, 0, 0, (uint32_t) offsetof( KeyedConfig, scores ), (uint32_t) sizeof( ( (KeyedConfig *) 0 )->scores ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_score_board_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "teams", "teams", "TeamConfig", 0xbaaeb048a5a8fa6dull, 13, 1, 0, 0, (int32_t) TEAM_MAX, (uint32_t) offsetof( KeyedConfig, teams ), (uint32_t) sizeof( ( (KeyedConfig *) 0 )->teams[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_team_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, "Team", schema_tabledemo_keyed_config_teams_keys_, 3, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "hulls", "hulls", "HullConfig", 0xce0ac3c25694d8ffull, 13, 1, 0, 0, (int32_t) HULL_MAX, (uint32_t) offsetof( KeyedConfig, hulls ), (uint32_t) sizeof( ( (KeyedConfig *) 0 )->hulls[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_hull_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, "Hull", schema_tabledemo_keyed_config_hulls_keys_, 3, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "scores", "scores", "ScoreBoard", 0x01986b0b27400fb2ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( KeyedConfig, scores ), (uint32_t) sizeof( ( (KeyedConfig *) 0 )->scores ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_score_board_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_keyed_config_info_ = { "KeyedConfig", (uint32_t) sizeof( KeyedConfig ), 3, schema_tabledemo_keyed_config_fields_, schema_tabledemo_keyed_config_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_tabledemo_score_board_per_team_keys_[] = { - { "None", 0x0000 }, - { "Red", 0xbb03 }, - { "Blue", 0xf630 }, - { "Green", 0x6e0f }, + { "None", 0x0000000000000000ull }, + { "Red", 0x9ff1de19feac1b7cull }, + { "Blue", 0xecf3d3a7c1693e2dull }, + { "Green", 0xcf00d78fd5953f1cull }, }; static const TableFieldInfo schema_tabledemo_score_board_fields_[] = { - { "per_team", "per_team", "int32", 0x443f, 4, 1, 0, 0, (int32_t) TEAM_MAX, (uint32_t) offsetof( ScoreBoard, per_team ), (uint32_t) sizeof( ( (ScoreBoard *) 0 )->per_team[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100000.0, -1, NULL, 0, "Team", schema_tabledemo_score_board_per_team_keys_, 3, NULL, "", TableDocNone, 0, NULL }, + { "per_team", "per_team", "int32", 0xf10fad739a0e1660ull, 4, 1, 0, 0, (int32_t) TEAM_MAX, (uint32_t) offsetof( ScoreBoard, per_team ), (uint32_t) sizeof( ( (ScoreBoard *) 0 )->per_team[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100000.0, NULL, 0, -1, NULL, 0, "Team", schema_tabledemo_score_board_per_team_keys_, 3, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_score_board_info_ = { "ScoreBoard", (uint32_t) sizeof( ScoreBoard ), 1, schema_tabledemo_score_board_fields_, schema_tabledemo_score_board_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/examples-c/KeyedTable.h b/testdata/golden/tables/examples-c/KeyedTable.h index 704125bb3..18b61160b 100644 --- a/testdata/golden/tables/examples-c/KeyedTable.h +++ b/testdata/golden/tables/examples-c/KeyedTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package tabledemo — protocol id 0xba7b344f28584d33 (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_TABLEDEMO_KEYEDTABLE_H #define SCHEMA_TABLEDEMO_KEYEDTABLE_H @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -183,6 +194,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -210,6 +223,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -231,157 +246,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[156]; + uint32_t slots[512], positions[156]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (512-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(512-1); + } + if(v->count==156) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } } - return 0; + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -391,6 +526,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_TABLEDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_TABLEDEMO_BUILD_VERSION #define SCHEMA_TABLEDEMO_BUILD_VERSION @@ -502,25 +692,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -530,40 +720,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_TABLEDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table TeamConfig — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in team_config_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -688,84 +1459,199 @@ static SCHEMA_UNUSED int64_t gunner_config_measure( const GunnerConfig * value ) static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_config_save_body( TableWriter * w, const GunnerConfig * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_config_load_body( TableReader * r, GunnerConfig * value ); static SCHEMA_UNUSED int64_t turret_config_measure( const TurretConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int turret_config_save_body( TableWriter * w, const TurretConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int turret_config_load_body( TableReader * r, TurretConfig * value ); +static SCHEMA_UNUSED int turret_config_save_body( TableWriter * w, const TurretConfig * value ); +static SCHEMA_UNUSED int turret_config_load_body( TableReader * r, TurretConfig * value ); static SCHEMA_UNUSED int64_t hull_config_measure( const HullConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int hull_config_save_body( TableWriter * w, const HullConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int hull_config_load_body( TableReader * r, HullConfig * value ); +static SCHEMA_UNUSED int hull_config_save_body( TableWriter * w, const HullConfig * value ); +static SCHEMA_UNUSED int hull_config_load_body( TableReader * r, HullConfig * value ); static SCHEMA_UNUSED int64_t keyed_config_measure( const KeyedConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int keyed_config_save_body( TableWriter * w, const KeyedConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int keyed_config_load_body( TableReader * r, KeyedConfig * value ); +static SCHEMA_UNUSED int keyed_config_save_body( TableWriter * w, const KeyedConfig * value ); +static SCHEMA_UNUSED int keyed_config_load_body( TableReader * r, KeyedConfig * value ); static SCHEMA_UNUSED int64_t score_board_measure( const ScoreBoard * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int score_board_save_body( TableWriter * w, const ScoreBoard * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int score_board_load_body( TableReader * r, ScoreBoard * value ); -static SCHEMA_UNUSED int64_t team_config_measure( const TeamConfig * value ) +static SCHEMA_UNUSED int schema_tabledemo_team_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_gunner_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_turret_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_hull_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_keyed_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_score_board_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int team_config_save_message_body(TableBitWriter *,const TeamConfig *); +static SCHEMA_UNUSED int gunner_config_save_message_body(TableBitWriter *,const GunnerConfig *); +static SCHEMA_UNUSED int turret_config_save_message_body(TableBitWriter *,const TurretConfig *); +static SCHEMA_UNUSED int hull_config_save_message_body(TableBitWriter *,const HullConfig *); +static SCHEMA_UNUSED int keyed_config_save_message_body(TableBitWriter *,const KeyedConfig *); +static SCHEMA_UNUSED int score_board_save_message_body(TableBitWriter *,const ScoreBoard *); +static SCHEMA_UNUSED int team_config_load_message_body(TableMessageReader *,TeamConfig *); +static SCHEMA_UNUSED int gunner_config_load_message_body(TableMessageReader *,GunnerConfig *); +static SCHEMA_UNUSED int turret_config_load_message_body(TableMessageReader *,TurretConfig *); +static SCHEMA_UNUSED int hull_config_load_message_body(TableMessageReader *,HullConfig *); +static SCHEMA_UNUSED int keyed_config_load_message_body(TableMessageReader *,KeyedConfig *); +static SCHEMA_UNUSED int score_board_load_message_body(TableMessageReader *,ScoreBoard *); +static SCHEMA_UNUSED int schema_tabledemo_team_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_gunner_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_turret_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_hull_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_keyed_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_score_board_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_team_config_wire_banner_( TableWriter * w, const TeamConfig * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->spawn_count != 4 ) { bytes += 3 + 4; } /* spawn_count */ - if ( value->banner_length < 0 || value->banner_length > 16 ) { return -1; } /* storage invariant */ - if ( value->banner_length > 0 ) { bytes += 3 + 4 + value->banner_length; } /* banner */ - return bytes; + if ( value->banner_length < 0 || value->banner_length > 16 ) { return 0; } + table_writer_raw( w, value->banner, value->banner_length ); + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int team_config_save_body( TableWriter * w, const TeamConfig * value ) +static SCHEMA_UNUSED int team_config_save_body( TableWriter * w, const TeamConfig * value ) { - if ( value->spawn_count != 4 ) - { - table_writer_put16( w, 0x3668 ); table_writer_put8( w, 4 ); /* spawn_count */ - table_writer_put32( w, (uint32_t) ( value->spawn_count ) ); + (void) value; + { /* spawn_count */ + if ( !( value->spawn_count == 4 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xceec99e2d65db674ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->spawn_count ) ); + } } - if ( value->banner_length < 0 || value->banner_length > 16 ) { return 0; } /* storage invariant */ - if ( value->banner_length > 0 ) - { - table_writer_put16( w, 0x80e4 ); table_writer_put8( w, 12 ); /* banner */ - table_writer_put32( w, (uint32_t) value->banner_length ); - table_writer_raw( w, value->banner, value->banner_length ); + { /* banner */ + if ( value->banner_length < 0 || value->banner_length > 16 ) { return 0; } + if ( value->banner_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbca0dab1c7a00ccfull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_team_config_wire_banner_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_team_config_wire_banner_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_team_config_wire_banner_( w, value ) ) { return 0; } + } + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t team_config_measure( const TeamConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !team_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t team_config_save( const TeamConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !team_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == team_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int team_config_load_body( TableReader * r, TeamConfig * value ) +static SCHEMA_UNUSED int team_config_load_body( TableReader * r, TeamConfig * value ) { - team_config_reset( value ); /* prefill declared defaults in place, then overlay */ + team_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x3668: /* spawn_count */ + case 0xceec99e2d65db674ull: /* spawn_count */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; r->report->clamped++; } + value->spawn_count = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; r->report->clamped++; } + value->spawn_count = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 64 ) { decoded_v = 64; r->report->clamped++; } + value->spawn_count = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 64 ) { decoded_v = 64; r->report->clamped++; } - value->spawn_count = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; r->report->clamped++; } + value->spawn_count = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; r->report->clamped++; } + value->spawn_count = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 64 ) { decoded_v = 64; r->report->clamped++; } + value->spawn_count = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x80e4: /* banner */ + case 0xbca0dab1c7a00ccfull: /* banner */ { if ( kind != 12 ) { @@ -773,84 +1659,238 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int team_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 16 ) { keep = 16; r->report->clamped++; } - memcpy( value->banner, r->buffer + r->offset, keep ); - value->banner[keep] = 0; - value->banner_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->banner[0] = 0; value->banner_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 16 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 16 ); r->report->clamped++; } + memcpy( value->banner, sub.buffer, (size_t) keep ); value->banner[keep] = 0; value->banner_length = (int32_t) keep; break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int team_config_load( TeamConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + team_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return team_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t gunner_config_measure( const GunnerConfig * value ) +static SCHEMA_UNUSED int team_config_save_message_body(TableBitWriter * w,const TeamConfig * value) { - int64_t bytes = 2; /* terminator */ - if ( value->reaction != 0.2f ) { bytes += 3 + 4; } /* reaction */ - if ( value->tracking != 0 ) { bytes += 3 + 1; } /* tracking */ - return bytes; + (void)value; + { /* spawn_count */ + if(!(value->spawn_count == 4)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,98,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->spawn_count)-UINT64_C(0),7); + } + } + { /* banner */ + if(value->banner_length<0 || value->banner_length>16) return 0; + if(value->banner_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,99,kTableMessageRefBitsHere); + if(value->banner_length<0 || value->banner_length>16) return 0; + table_bit_put(w,(uint64_t)value->banner_length,5); + table_bit_align_write(w); + table_bit_putbytes(w,value->banner,value->banner_length); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_config_save_body( TableWriter * w, const GunnerConfig * value ) +static SCHEMA_UNUSED int64_t team_config_measure_messages(const TeamConfig * values,int64_t count,TableReport * report) { - if ( value->reaction != 0.2f ) - { - table_writer_put16( w, 0x900f ); table_writer_put8( w, 10 ); /* reaction */ - table_writer_put32( w, table_float_to_bits( value->reaction ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xceec99e2d65db674): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>64ll){decoded=64ll; + r->report->clamped++; + } + value->spawn_count=(int32_t)decoded; + } + break; + } + case UINT64_C(0xbca0dab1c7a00ccf): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,16); + memcpy(value->banner,text,(size_t)kept); + value->banner[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->banner_length=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int team_config_load_messages(TeamConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int gunner_config_save_body( TableWriter * w, const GunnerConfig * value ) +{ + (void) value; + { /* reaction */ + if ( !( value->reaction == 0.2f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb75aa3662201646aull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->reaction ) ); + } } - if ( value->tracking != 0 ) - { - table_writer_put16( w, 0x53d5 ); table_writer_put8( w, 1 ); /* tracking */ - table_writer_put8( w, value->tracking ? 1 : 0 ); + { /* tracking */ + if ( !( value->tracking == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa6bf719a4602b0bcull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->tracking ? 1 : 0 ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t gunner_config_measure( const GunnerConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !gunner_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t gunner_config_save( const GunnerConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !gunner_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == gunner_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_config_load_body( TableReader * r, GunnerConfig * value ) +static SCHEMA_UNUSED int gunner_config_load_body( TableReader * r, GunnerConfig * value ) { - gunner_config_reset( value ); /* prefill declared defaults in place, then overlay */ + gunner_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x900f: /* reaction */ + case 0xb75aa3662201646aull: /* reaction */ { if ( kind != 10 ) { @@ -858,11 +1898,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->reaction = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->reaction = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x53d5: /* tracking */ + case 0xa6bf719a4602b0bcull: /* tracking */ { if ( kind != 1 ) { @@ -870,89 +1918,243 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->tracking = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->tracking = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int gunner_config_load( GunnerConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + gunner_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return gunner_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t turret_config_measure( const TurretConfig * value ) +static SCHEMA_UNUSED int gunner_config_save_message_body(TableBitWriter * w,const GunnerConfig * value) { - int64_t bytes = 2; /* terminator */ - if ( value->damage != 10.0f ) { bytes += 3 + 4; } /* damage */ - if ( value->cooldown != 0.5f ) { bytes += 3 + 4; } /* cooldown */ - if ( value->gunner_present ) /* ?GunnerConfig: presence decides, not content */ - { - int64_t body_gunner = gunner_config_measure( &value->gunner ); - if ( body_gunner < 0 ) { return -1; } - bytes += 3 + 4 + body_gunner; /* gunner */ - } - return bytes; + (void)value; + { /* reaction */ + if(!(value->reaction == 0.2f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,11,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->reaction),32); + } + } + { /* tracking */ + if(!(value->tracking == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,12,kTableMessageRefBitsHere); + table_bit_put(w,value->tracking ? 1 : 0,1); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int turret_config_save_body( TableWriter * w, const TurretConfig * value ) +static SCHEMA_UNUSED int64_t gunner_config_measure_messages(const GunnerConfig * values,int64_t count,TableReport * report) { - if ( value->damage != 10.0f ) - { - table_writer_put16( w, 0x15a9 ); table_writer_put8( w, 10 ); /* damage */ - table_writer_put32( w, table_float_to_bits( value->damage ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xb75aa3662201646a): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->reaction=decoded; + } + break; + } + case UINT64_C(0xa6bf719a4602b0bc): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->tracking=lo!=0; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int gunner_config_load_messages(GunnerConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int turret_config_save_body( TableWriter * w, const TurretConfig * value ) +{ + (void) value; + { /* damage */ + if ( !( value->damage == 10.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7f6308be8ab37fc0ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->damage ) ); + } } - if ( value->cooldown != 0.5f ) - { - table_writer_put16( w, 0x2230 ); table_writer_put8( w, 10 ); /* cooldown */ - table_writer_put32( w, table_float_to_bits( value->cooldown ) ); + { /* cooldown */ + if ( !( value->cooldown == 0.5f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xdc2cbe6953343d48ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->cooldown ) ); + } } - if ( value->gunner_present ) /* ?GunnerConfig */ - { - int64_t body_gunner = gunner_config_measure( &value->gunner ); - if ( body_gunner < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - table_writer_put16( w, 0x2bc9 ); table_writer_put8( w, 13 ); /* gunner */ - table_writer_put32( w, (uint32_t) body_gunner ); - if ( !gunner_config_save_body( w, &value->gunner ) ) { return 0; } + { /* gunner */ + if ( value->gunner_present ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x40dbb648c0cd44aaull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !gunner_config_save_body( w, &value->gunner ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !gunner_config_save_body( &probe, &value->gunner ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !gunner_config_save_body( w, &value->gunner ) ) { return 0; } + } + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t turret_config_measure( const TurretConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !turret_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t turret_config_save( const TurretConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !turret_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == turret_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int turret_config_load_body( TableReader * r, TurretConfig * value ) +static SCHEMA_UNUSED int turret_config_load_body( TableReader * r, TurretConfig * value ) { - turret_config_reset( value ); /* prefill declared defaults in place, then overlay */ + turret_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x15a9: /* damage */ + case 0x7f6308be8ab37fc0ull: /* damage */ { if ( kind != 10 ) { @@ -960,11 +2162,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int turret_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->damage = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->damage = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x2230: /* cooldown */ + case 0xdc2cbe6953343d48ull: /* cooldown */ { if ( kind != 10 ) { @@ -972,11 +2182,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int turret_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->cooldown = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->cooldown = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x2bc9: /* gunner */ + case 0x40dbb648c0cd44aaull: /* gunner */ { if ( kind != 13 ) { @@ -984,161 +2202,316 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int turret_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - gunner_config_load_body( &sub, &value->gunner ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + gunner_config_load_body( &sub, &value->gunner ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; gunner_config_reset( &value->gunner ); } value->gunner_present = 1; break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int turret_config_load( TurretConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + turret_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return turret_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t hull_config_measure( const HullConfig * value ) +static SCHEMA_UNUSED int turret_config_save_message_body(TableBitWriter * w,const TurretConfig * value) +{ + (void)value; + { /* damage */ + if(!(value->damage == 10.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,100,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->damage),32); + } + } + { /* cooldown */ + if(!(value->cooldown == 0.5f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,101,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->cooldown),32); + } + } + { /* gunner */ + if(value->gunner_present) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,97,kTableMessageRefBitsHere); + if(!gunner_config_save_message_body(w,&value->gunner)) return 0; + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t turret_config_measure_messages(const TurretConfig * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ihealth != 100.0f ) { bytes += 3 + 4; } /* health */ - if ( value->mass != 1.0f ) { bytes += 3 + 4; } /* mass */ + turret_config_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x7f6308be8ab37fc0): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->damage=decoded; + } + break; + } + case UINT64_C(0xdc2cbe6953343d48): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->cooldown=decoded; + } + break; + } + case UINT64_C(0x40dbb648c0cd44aa): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!gunner_config_load_message_body(r,&value->gunner))return 0; + value->gunner_present=1; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int turret_config_load_messages(TurretConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +#define SCHEMA_TABLEDEMO_HULL_CONFIG_TURRETS_AT(value,key) SCHEMA_TABLE_KEYED_AT((value).turrets,(key),WEAPON_MAX) +static SCHEMA_UNUSED int schema_tabledemo_hull_config_wire_turrets_( TableWriter * w, const HullConfig * value ) +{ + uint64_t count = 0; int32_t i; + for ( i = 0; i < WEAPON_MAX; i++ ) + { + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !turret_config_save_body( &slot_probe, &value->turrets[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + count++; + } + table_writer_put8( w, 13 ); table_writer_leb( w, count ); + for ( i = 0; i < WEAPON_MAX; i++ ) { - int64_t pairs_turrets = 0, body_turrets = 0; - int32_t i; - for ( i = 0; i < WEAPON_MAX; i++ ) /* [Weapon]: every stored slot is a named variant's */ + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !turret_config_save_body( &slot_probe, &value->turrets[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + switch ( i + 1 ) { - int64_t elem_bytes = turret_config_measure( &value->turrets[i] ); - if ( elem_bytes < 0 ) { return -1; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Weapon) ( i + 1 ) ) /* Weapon rides as the u16 hash of its VARIANT NAME (§5) */ - { - case WEAPON_NONE: key_id = 0; break; - case WEAPON_CANNON: key_id = 0xf055; break; - case WEAPON_MISSILE: key_id = 0x16b3; break; - case WEAPON_MINE: key_id = 0x61bf; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_turrets++; body_turrets += 2 + 4 + elem_bytes; /* key, length, body */ + case WEAPON_NONE: table_writer_leb( w, 0 ); break; + case WEAPON_CANNON: table_writer_id( w, 0x5854debe3b2e767cull ); break; + case WEAPON_MISSILE: table_writer_id( w, 0xb528592e5a4583e3ull ); break; + case WEAPON_MINE: table_writer_id( w, 0x04dc16aea8ff5276ull ); break; + default: return 0; /* no declared identity */ + } + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !turret_config_save_body( w, &value->turrets[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !turret_config_save_body( &probe, &value->turrets[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !turret_config_save_body( w, &value->turrets[i] ) ) { return 0; } } - if ( pairs_turrets > 0 ) { bytes += 3 + 4 + 5 + body_turrets; } /* turrets */ } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int hull_config_save_body( TableWriter * w, const HullConfig * value ) +static SCHEMA_UNUSED int hull_config_save_body( TableWriter * w, const HullConfig * value ) { - if ( value->health != 100.0f ) - { - table_writer_put16( w, 0x8617 ); table_writer_put8( w, 10 ); /* health */ - table_writer_put32( w, table_float_to_bits( value->health ) ); + (void) value; + { /* health */ + if ( !( value->health == 100.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7f69d4b5288ba9cfull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->health ) ); + } } - if ( value->mass != 1.0f ) - { - table_writer_put16( w, 0xe7a6 ); table_writer_put8( w, 10 ); /* mass */ - table_writer_put32( w, table_float_to_bits( value->mass ) ); + { /* mass */ + if ( !( value->mass == 1.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1f3757a2ce7b0ab1ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->mass ) ); + } } - { - uint32_t pairs_turrets = 0; - int32_t i; - for ( i = 0; i < WEAPON_MAX; i++ ) /* [Weapon]: every stored slot is a named variant's */ + { /* turrets */ + int rides = 0; int32_t i; + for ( i = 0; i < WEAPON_MAX; i++ ) { - int64_t elem_bytes = turret_config_measure( &value->turrets[i] ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Weapon) ( i + 1 ) ) /* Weapon rides as the u16 hash of its VARIANT NAME (§5) */ - { - case WEAPON_NONE: key_id = 0; break; - case WEAPON_CANNON: key_id = 0xf055; break; - case WEAPON_MISSILE: key_id = 0x16b3; break; - case WEAPON_MINE: key_id = 0x61bf; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_turrets++; + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !turret_config_save_body( &slot_probe, &value->turrets[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + rides = 1; break; } - if ( pairs_turrets > 0 ) + if ( rides ) { - int64_t len_at_turrets; - /* KIND 16, not 14: a keyed body and a positional one are - incompatible, so a reader of the other kind must see a kind - mismatch and skip, never misdecode (docs/SPEC-TABLES.md §3.2) */ - table_writer_put16( w, 0x48ad ); table_writer_put8( w, 16 ); /* turrets (keyed by Weapon) */ - len_at_turrets = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, pairs_turrets ); - /* ASCENDING BY VARIANT ORDINAL, which is slot order — this - writer's choice, and a reader must not rely on it: every - slot is found by its key (docs/SPEC-TABLES.md §3.2) */ - for ( i = 0; i < WEAPON_MAX; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x84f8260bc283608cull ); table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at_turrets; - int64_t elem_bytes = turret_config_measure( &value->turrets[i] ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Weapon) ( i + 1 ) ) /* Weapon rides as the u16 hash of its VARIANT NAME (§5) */ - { - case WEAPON_NONE: key_id = 0; break; - case WEAPON_CANNON: key_id = 0xf055; break; - case WEAPON_MISSILE: key_id = 0x16b3; break; - case WEAPON_MINE: key_id = 0x61bf; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - table_writer_put16( w, key_id ); /* the slot's VARIANT id, not its position */ - elem_len_at_turrets = w->offset; table_writer_put32( w, 0 ); - if ( !turret_config_save_body( w, &value->turrets[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at_turrets, (uint32_t) ( w->offset - elem_len_at_turrets - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_hull_config_wire_turrets_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_hull_config_wire_turrets_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_hull_config_wire_turrets_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_turrets, (uint32_t) ( w->offset - len_at_turrets - 4 ) ); } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t hull_config_measure( const HullConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !hull_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t hull_config_save( const HullConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !hull_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == hull_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int hull_config_load_body( TableReader * r, HullConfig * value ) +static SCHEMA_UNUSED int hull_config_load_body( TableReader * r, HullConfig * value ) { - hull_config_reset( value ); /* prefill declared defaults in place, then overlay */ + hull_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x8617: /* health */ + case 0x7f69d4b5288ba9cfull: /* health */ { if ( kind != 10 ) { @@ -1146,11 +2519,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int hull_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->health = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->health = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xe7a6: /* mass */ + case 0x1f3757a2ce7b0ab1ull: /* mass */ { if ( kind != 10 ) { @@ -1158,11 +2539,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int hull_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->mass = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->mass = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x48ad: /* turrets */ + case 0x84f8260bc283608cull: /* turrets */ { if ( kind != 16 ) { @@ -1170,291 +2559,483 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int hull_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - TableReader sub; - uint32_t i; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); + uint8_t elem_kind; uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) { if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } r->report->widened++; } for ( i = 0; i < count; i++ ) { - uint16_t key; - uint32_t elem_len; - Weapon slot; - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - key = table_reader_get16( &sub ); - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - if ( key == 0 ) - { - /* None is the NULL KEY: 0 is the reserved id no declared - name can fold to, so a body carrying one is DAMAGED, not - merely foreign. Framing damage stops this body, keeps what - it decoded, and the parent reads on past the length - (docs/SPEC-TABLES.md §3.2, §4). */ - r->report->malformed = 1; - break; - } - slot = WEAPON_NONE; - { - int32_t before = r->report->unknown; - switch ( key ) - { - case 0: slot = WEAPON_NONE; break; - case 0xf055: slot = WEAPON_CANNON; break; - case 0x16b3: slot = WEAPON_MISSILE; break; - case 0x61bf: slot = WEAPON_MINE; break; - default: slot = WEAPON_NONE; r->report->unknown++; break; /* an id this build cannot name */ - } - if ( r->report->unknown != before ) - { - sub.offset += elem_len; /* a slot this reader cannot name */ - continue; - } - } + uint64_t key_ref, key; int32_t slot = -1; TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - turret_config_load_body( &elem, &value->turrets[(int32_t) slot - 1] ); + case 0x5854debe3b2e767cull: slot = 0; break; + case 0xb528592e5a4583e3ull: slot = 1; break; + case 0x04dc16aea8ff5276ull: slot = 2; break; + default: r->report->unknown++; break; } - sub.offset += elem_len; + if ( slot < 0 ) { continue; } + turret_config_load_body( &elem, &value->turrets[slot] ); + if(elem.offset!=elem.size) { r->report->malformed=1; turret_config_reset(&value->turrets[slot]); } } } - r->offset = body_end; /* unread pairs and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int hull_config_load( HullConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + hull_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return hull_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t keyed_config_measure( const KeyedConfig * value ) +static SCHEMA_UNUSED int hull_config_save_message_body(TableBitWriter * w,const HullConfig * value) { - int64_t bytes = 2; /* terminator */ + (void)value; + { /* health */ + if(!(value->health == 100.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->health),32); + } + } + { /* mass */ + if(!(value->mass == 1.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,15,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->mass),32); + } + } + { /* turrets */ + int rides=0; + { int32_t i; + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!turret_config_save_message_body(&probe,&value->turrets[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,16,kTableMessageRefBitsHere); + { uint32_t count=0; + int32_t i; + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!turret_config_save_message_body(&probe,&value->turrets[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + count++; + } + table_bit_put(w,count,2); + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!turret_config_save_message_body(&probe,&value->turrets[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + switch(i+1) { + case WEAPON_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case WEAPON_CANNON: table_bit_put(w,125,kTableMessageRefBitsHere); + break; + case WEAPON_MISSILE: table_bit_put(w,126,kTableMessageRefBitsHere); + break; + case WEAPON_MINE: table_bit_put(w,127,kTableMessageRefBitsHere); + break; + default: return 0; + } + if(!turret_config_save_message_body(w,&value->turrets[i])) return 0; + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t hull_config_measure_messages(const HullConfig * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x7f69d4b5288ba9cf): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->health=decoded; + } + break; + } + case UINT64_C(0x1f3757a2ce7b0ab1): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->mass=decoded; + } + break; + } + case UINT64_C(0x84f8260bc283608c): { + if(!(entry->kind==16 && entry->elem_kind==13)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0x5854debe3b2e767c):slot=0; + break; + case UINT64_C(0xb528592e5a4583e3):slot=1; + break; + case UINT64_C(0x04dc16aea8ff5276):slot=2; + break; + default:break; + } if(slot<0){r->report->unknown++; + { TurretConfig scratch; + memset(&scratch,0,sizeof(scratch)); + if(!turret_config_load_message_body(r,&scratch))return 0; + }continue; + } + if(!turret_config_load_message_body(r,&value->turrets[slot]))return 0; + } } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int hull_config_load_messages(HullConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +#define SCHEMA_TABLEDEMO_KEYED_CONFIG_TEAMS_AT(value,key) SCHEMA_TABLE_KEYED_AT((value).teams,(key),TEAM_MAX) +#define SCHEMA_TABLEDEMO_KEYED_CONFIG_HULLS_AT(value,key) SCHEMA_TABLE_KEYED_AT((value).hulls,(key),HULL_MAX) +static SCHEMA_UNUSED int schema_tabledemo_keyed_config_wire_teams_( TableWriter * w, const KeyedConfig * value ) +{ + uint64_t count = 0; int32_t i; + for ( i = 0; i < TEAM_MAX; i++ ) { - int64_t pairs_teams = 0, body_teams = 0; - int32_t i; - for ( i = 0; i < TEAM_MAX; i++ ) /* [Team]: every stored slot is a named variant's */ - { - int64_t elem_bytes = team_config_measure( &value->teams[i] ); - if ( elem_bytes < 0 ) { return -1; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_teams++; body_teams += 2 + 4 + elem_bytes; /* key, length, body */ - } - if ( pairs_teams > 0 ) { bytes += 3 + 4 + 5 + body_teams; } /* teams */ + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !team_config_save_body( &slot_probe, &value->teams[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + count++; } + table_writer_put8( w, 13 ); table_writer_leb( w, count ); + for ( i = 0; i < TEAM_MAX; i++ ) { - int64_t pairs_hulls = 0, body_hulls = 0; - int32_t i; - for ( i = 0; i < HULL_MAX; i++ ) /* [Hull]: every stored slot is a named variant's */ + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !team_config_save_body( &slot_probe, &value->teams[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + switch ( i + 1 ) { - int64_t elem_bytes = hull_config_measure( &value->hulls[i] ); - if ( elem_bytes < 0 ) { return -1; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Hull) ( i + 1 ) ) /* Hull rides as the u16 hash of its VARIANT NAME (§5) */ - { - case HULL_NONE: key_id = 0; break; - case HULL_INTERCEPTOR: key_id = 0xf0f0; break; - case HULL_GUNSHIP: key_id = 0xb534; break; - case HULL_FREIGHTER: key_id = 0xb617; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_hulls++; body_hulls += 2 + 4 + elem_bytes; /* key, length, body */ + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + default: return 0; /* no declared identity */ + } + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !team_config_save_body( w, &value->teams[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !team_config_save_body( &probe, &value->teams[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !team_config_save_body( w, &value->teams[i] ) ) { return 0; } } - if ( pairs_hulls > 0 ) { bytes += 3 + 4 + 5 + body_hulls; } /* hulls */ } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_keyed_config_wire_hulls_( TableWriter * w, const KeyedConfig * value ) +{ + uint64_t count = 0; int32_t i; + for ( i = 0; i < HULL_MAX; i++ ) + { + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !hull_config_save_body( &slot_probe, &value->hulls[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + count++; + } + table_writer_put8( w, 13 ); table_writer_leb( w, count ); + for ( i = 0; i < HULL_MAX; i++ ) { - int64_t body_scores = score_board_measure( &value->scores ); - if ( body_scores < 0 ) { return -1; } - if ( body_scores > 2 ) { bytes += 3 + 4 + body_scores; } /* scores: all-default nested elides */ + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !hull_config_save_body( &slot_probe, &value->hulls[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + switch ( i + 1 ) + { + case HULL_NONE: table_writer_leb( w, 0 ); break; + case HULL_INTERCEPTOR: table_writer_id( w, 0xae61a6cbe88c2cf4ull ); break; + case HULL_GUNSHIP: table_writer_id( w, 0x334d24e1420dbc1full ); break; + case HULL_FREIGHTER: table_writer_id( w, 0x6c2321a3d00e23dbull ); break; + default: return 0; /* no declared identity */ + } + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !hull_config_save_body( w, &value->hulls[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !hull_config_save_body( &probe, &value->hulls[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !hull_config_save_body( w, &value->hulls[i] ) ) { return 0; } + } } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int keyed_config_save_body( TableWriter * w, const KeyedConfig * value ) +static SCHEMA_UNUSED int keyed_config_save_body( TableWriter * w, const KeyedConfig * value ) { - { - uint32_t pairs_teams = 0; - int32_t i; - for ( i = 0; i < TEAM_MAX; i++ ) /* [Team]: every stored slot is a named variant's */ + (void) value; + { /* teams */ + int rides = 0; int32_t i; + for ( i = 0; i < TEAM_MAX; i++ ) { - int64_t elem_bytes = team_config_measure( &value->teams[i] ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_teams++; + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !team_config_save_body( &slot_probe, &value->teams[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + rides = 1; break; } - if ( pairs_teams > 0 ) + if ( rides ) { - int64_t len_at_teams; - /* KIND 16, not 14: a keyed body and a positional one are - incompatible, so a reader of the other kind must see a kind - mismatch and skip, never misdecode (docs/SPEC-TABLES.md §3.2) */ - table_writer_put16( w, 0x9ae1 ); table_writer_put8( w, 16 ); /* teams (keyed by Team) */ - len_at_teams = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, pairs_teams ); - /* ASCENDING BY VARIANT ORDINAL, which is slot order — this - writer's choice, and a reader must not rely on it: every - slot is found by its key (docs/SPEC-TABLES.md §3.2) */ - for ( i = 0; i < TEAM_MAX; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbaaeb048a5a8fa6dull ); table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at_teams; - int64_t elem_bytes = team_config_measure( &value->teams[i] ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - table_writer_put16( w, key_id ); /* the slot's VARIANT id, not its position */ - elem_len_at_teams = w->offset; table_writer_put32( w, 0 ); - if ( !team_config_save_body( w, &value->teams[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at_teams, (uint32_t) ( w->offset - elem_len_at_teams - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_keyed_config_wire_teams_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_keyed_config_wire_teams_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_keyed_config_wire_teams_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_teams, (uint32_t) ( w->offset - len_at_teams - 4 ) ); } } - { - uint32_t pairs_hulls = 0; - int32_t i; - for ( i = 0; i < HULL_MAX; i++ ) /* [Hull]: every stored slot is a named variant's */ + { /* hulls */ + int rides = 0; int32_t i; + for ( i = 0; i < HULL_MAX; i++ ) { - int64_t elem_bytes = hull_config_measure( &value->hulls[i] ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Hull) ( i + 1 ) ) /* Hull rides as the u16 hash of its VARIANT NAME (§5) */ - { - case HULL_NONE: key_id = 0; break; - case HULL_INTERCEPTOR: key_id = 0xf0f0; break; - case HULL_GUNSHIP: key_id = 0xb534; break; - case HULL_FREIGHTER: key_id = 0xb617; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_hulls++; + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !hull_config_save_body( &slot_probe, &value->hulls[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + rides = 1; break; } - if ( pairs_hulls > 0 ) + if ( rides ) { - int64_t len_at_hulls; - /* KIND 16, not 14: a keyed body and a positional one are - incompatible, so a reader of the other kind must see a kind - mismatch and skip, never misdecode (docs/SPEC-TABLES.md §3.2) */ - table_writer_put16( w, 0xeff5 ); table_writer_put8( w, 16 ); /* hulls (keyed by Hull) */ - len_at_hulls = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, pairs_hulls ); - /* ASCENDING BY VARIANT ORDINAL, which is slot order — this - writer's choice, and a reader must not rely on it: every - slot is found by its key (docs/SPEC-TABLES.md §3.2) */ - for ( i = 0; i < HULL_MAX; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xce0ac3c25694d8ffull ); table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at_hulls; - int64_t elem_bytes = hull_config_measure( &value->hulls[i] ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Hull) ( i + 1 ) ) /* Hull rides as the u16 hash of its VARIANT NAME (§5) */ - { - case HULL_NONE: key_id = 0; break; - case HULL_INTERCEPTOR: key_id = 0xf0f0; break; - case HULL_GUNSHIP: key_id = 0xb534; break; - case HULL_FREIGHTER: key_id = 0xb617; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - table_writer_put16( w, key_id ); /* the slot's VARIANT id, not its position */ - elem_len_at_hulls = w->offset; table_writer_put32( w, 0 ); - if ( !hull_config_save_body( w, &value->hulls[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at_hulls, (uint32_t) ( w->offset - elem_len_at_hulls - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_keyed_config_wire_hulls_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_keyed_config_wire_hulls_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_keyed_config_wire_hulls_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_hulls, (uint32_t) ( w->offset - len_at_hulls - 4 ) ); } } - { - int64_t body_scores = score_board_measure( &value->scores ); - if ( body_scores < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_scores > 2 ) /* all-default nested elides */ + { /* scores */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !score_board_save_body( &default_probe, &value->scores ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0xdcf3 ); table_writer_put8( w, 13 ); /* scores */ - table_writer_put32( w, (uint32_t) body_scores ); - if ( !score_board_save_body( w, &value->scores ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x01986b0b27400fb2ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !score_board_save_body( w, &value->scores ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !score_board_save_body( &probe, &value->scores ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !score_board_save_body( w, &value->scores ) ) { return 0; } + } } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t keyed_config_measure( const KeyedConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !keyed_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t keyed_config_save( const KeyedConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !keyed_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == keyed_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int keyed_config_load_body( TableReader * r, KeyedConfig * value ) +static SCHEMA_UNUSED int keyed_config_load_body( TableReader * r, KeyedConfig * value ) { - keyed_config_reset( value ); /* prefill declared defaults in place, then overlay */ + keyed_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x9ae1: /* teams */ + case 0xbaaeb048a5a8fa6dull: /* teams */ { if ( kind != 16 ) { @@ -1462,68 +3043,33 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int keyed_config_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - TableReader sub; - uint32_t i; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); + uint8_t elem_kind; uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) { if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } r->report->widened++; } for ( i = 0; i < count; i++ ) { - uint16_t key; - uint32_t elem_len; - Team slot; - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - key = table_reader_get16( &sub ); - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - if ( key == 0 ) + uint64_t key_ref, key; int32_t slot = -1; TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) { - /* None is the NULL KEY: 0 is the reserved id no declared - name can fold to, so a body carrying one is DAMAGED, not - merely foreign. Framing damage stops this body, keeps what - it decoded, and the parent reads on past the length - (docs/SPEC-TABLES.md §3.2, §4). */ - r->report->malformed = 1; - break; + case 0x9ff1de19feac1b7cull: slot = 0; break; + case 0xecf3d3a7c1693e2dull: slot = 1; break; + case 0xcf00d78fd5953f1cull: slot = 2; break; + default: r->report->unknown++; break; } - slot = TEAM_NONE; - { - int32_t before = r->report->unknown; - switch ( key ) - { - case 0: slot = TEAM_NONE; break; - case 0xbb03: slot = TEAM_RED; break; - case 0xf630: slot = TEAM_BLUE; break; - case 0x6e0f: slot = TEAM_GREEN; break; - default: slot = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ - } - if ( r->report->unknown != before ) - { - sub.offset += elem_len; /* a slot this reader cannot name */ - continue; - } - } - { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - team_config_load_body( &elem, &value->teams[(int32_t) slot - 1] ); - } - sub.offset += elem_len; + if ( slot < 0 ) { continue; } + team_config_load_body( &elem, &value->teams[slot] ); + if(elem.offset!=elem.size) { r->report->malformed=1; team_config_reset(&value->teams[slot]); } } } - r->offset = body_end; /* unread pairs and slack skip via the length */ break; } - case 0xeff5: /* hulls */ + case 0xce0ac3c25694d8ffull: /* hulls */ { if ( kind != 16 ) { @@ -1531,68 +3077,33 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int keyed_config_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - TableReader sub; - uint32_t i; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); + uint8_t elem_kind; uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) { if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } r->report->widened++; } for ( i = 0; i < count; i++ ) { - uint16_t key; - uint32_t elem_len; - Hull slot; - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - key = table_reader_get16( &sub ); - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - if ( key == 0 ) - { - /* None is the NULL KEY: 0 is the reserved id no declared - name can fold to, so a body carrying one is DAMAGED, not - merely foreign. Framing damage stops this body, keeps what - it decoded, and the parent reads on past the length - (docs/SPEC-TABLES.md §3.2, §4). */ - r->report->malformed = 1; - break; - } - slot = HULL_NONE; - { - int32_t before = r->report->unknown; - switch ( key ) - { - case 0: slot = HULL_NONE; break; - case 0xf0f0: slot = HULL_INTERCEPTOR; break; - case 0xb534: slot = HULL_GUNSHIP; break; - case 0xb617: slot = HULL_FREIGHTER; break; - default: slot = HULL_NONE; r->report->unknown++; break; /* an id this build cannot name */ - } - if ( r->report->unknown != before ) - { - sub.offset += elem_len; /* a slot this reader cannot name */ - continue; - } - } + uint64_t key_ref, key; int32_t slot = -1; TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - hull_config_load_body( &elem, &value->hulls[(int32_t) slot - 1] ); + case 0xae61a6cbe88c2cf4ull: slot = 0; break; + case 0x334d24e1420dbc1full: slot = 1; break; + case 0x6c2321a3d00e23dbull: slot = 2; break; + default: r->report->unknown++; break; } - sub.offset += elem_len; + if ( slot < 0 ) { continue; } + hull_config_load_body( &elem, &value->hulls[slot] ); + if(elem.offset!=elem.size) { r->report->malformed=1; hull_config_reset(&value->hulls[slot]); } } } - r->offset = body_end; /* unread pairs and slack skip via the length */ break; } - case 0xdcf3: /* scores */ + case 0x01986b0b27400fb2ull: /* scores */ { if ( kind != 13 ) { @@ -1600,142 +3111,410 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int keyed_config_load_body( T if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - score_board_load_body( &sub, &value->scores ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + score_board_load_body( &sub, &value->scores ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; score_board_reset( &value->scores ); } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int keyed_config_load( KeyedConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + keyed_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return keyed_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t score_board_measure( const ScoreBoard * value ) +static SCHEMA_UNUSED int keyed_config_save_message_body(TableBitWriter * w,const KeyedConfig * value) +{ + (void)value; + { /* teams */ + int rides=0; + { int32_t i; + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!team_config_save_message_body(&probe,&value->teams[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,17,kTableMessageRefBitsHere); + { uint32_t count=0; + int32_t i; + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!team_config_save_message_body(&probe,&value->teams[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + count++; + } + table_bit_put(w,count,2); + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!team_config_save_message_body(&probe,&value->teams[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + switch(i+1) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,122,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,123,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,124,kTableMessageRefBitsHere); + break; + default: return 0; + } + if(!team_config_save_message_body(w,&value->teams[i])) return 0; + } } + } + } + { /* hulls */ + int rides=0; + { int32_t i; + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!hull_config_save_message_body(&probe,&value->hulls[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,18,kTableMessageRefBitsHere); + { uint32_t count=0; + int32_t i; + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!hull_config_save_message_body(&probe,&value->hulls[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + count++; + } + table_bit_put(w,count,2); + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!hull_config_save_message_body(&probe,&value->hulls[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + switch(i+1) { + case HULL_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case HULL_INTERCEPTOR: table_bit_put(w,116,kTableMessageRefBitsHere); + break; + case HULL_GUNSHIP: table_bit_put(w,117,kTableMessageRefBitsHere); + break; + case HULL_FREIGHTER: table_bit_put(w,118,kTableMessageRefBitsHere); + break; + default: return 0; + } + if(!hull_config_save_message_body(w,&value->hulls[i])) return 0; + } } + } + } + { /* scores */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!score_board_save_message_body(&probe,&value->scores)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,19,kTableMessageRefBitsHere); + if(!score_board_save_message_body(w,&value->scores)) return 0; + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t keyed_config_measure_messages(const KeyedConfig * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xbaaeb048a5a8fa6d): { + if(!(entry->kind==16 && entry->elem_kind==13)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0x9ff1de19feac1b7c):slot=0; + break; + case UINT64_C(0xecf3d3a7c1693e2d):slot=1; + break; + case UINT64_C(0xcf00d78fd5953f1c):slot=2; + break; + default:break; + } if(slot<0){r->report->unknown++; + { TeamConfig scratch; + memset(&scratch,0,sizeof(scratch)); + if(!team_config_load_message_body(r,&scratch))return 0; + }continue; + } + if(!team_config_load_message_body(r,&value->teams[slot]))return 0; + } } + break; + } + case UINT64_C(0xce0ac3c25694d8ff): { + if(!(entry->kind==16 && entry->elem_kind==13)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0xae61a6cbe88c2cf4):slot=0; + break; + case UINT64_C(0x334d24e1420dbc1f):slot=1; + break; + case UINT64_C(0x6c2321a3d00e23db):slot=2; + break; + default:break; + } if(slot<0){r->report->unknown++; + { HullConfig scratch; + memset(&scratch,0,sizeof(scratch)); + if(!hull_config_load_message_body(r,&scratch))return 0; + }continue; + } + if(!hull_config_load_message_body(r,&value->hulls[slot]))return 0; + } } + break; + } + case UINT64_C(0x01986b0b27400fb2): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!score_board_load_message_body(r,&value->scores))return 0; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int keyed_config_load_messages(KeyedConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +#define SCHEMA_TABLEDEMO_SCORE_BOARD_PER_TEAM_AT(value,key) SCHEMA_TABLE_KEYED_AT((value).per_team,(key),TEAM_MAX) +static SCHEMA_UNUSED int schema_tabledemo_score_board_wire_per_team_( TableWriter * w, const ScoreBoard * value ) { - int64_t bytes = 2; /* terminator */ + uint64_t count = 0; int32_t i; + for ( i = 0; i < TEAM_MAX; i++ ) + { + if ( value->per_team[i] == 0 ) { continue; } + count++; + } + table_writer_put8( w, 4 ); table_writer_leb( w, count ); + for ( i = 0; i < TEAM_MAX; i++ ) { - int64_t pairs_per_team = 0, body_per_team = 0; - int32_t i; - for ( i = 0; i < TEAM_MAX; i++ ) /* [Team]: every stored slot is a named variant's */ + if ( value->per_team[i] == 0 ) { continue; } + switch ( i + 1 ) { - if ( value->per_team[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_per_team++; body_per_team += 2 + 4 + 4; /* key, length, element */ + case TEAM_NONE: table_writer_leb( w, 0 ); break; + case TEAM_RED: table_writer_id( w, 0x9ff1de19feac1b7cull ); break; + case TEAM_BLUE: table_writer_id( w, 0xecf3d3a7c1693e2dull ); break; + case TEAM_GREEN: table_writer_id( w, 0xcf00d78fd5953f1cull ); break; + default: return 0; /* no declared identity */ + } + { + TableWriter probe = table_writer_probe( w ); + TableWriter * outer = w; + w = &probe; + table_writer_put32( w, (uint32_t) ( value->per_team[i] ) ); + w = outer; table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + table_writer_put32( w, (uint32_t) ( value->per_team[i] ) ); } - if ( pairs_per_team > 0 ) { bytes += 3 + 4 + 5 + body_per_team; } /* per_team */ } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int score_board_save_body( TableWriter * w, const ScoreBoard * value ) +static SCHEMA_UNUSED int score_board_save_body( TableWriter * w, const ScoreBoard * value ) { - { - uint32_t pairs_per_team = 0; - int32_t i; - for ( i = 0; i < TEAM_MAX; i++ ) /* [Team]: every stored slot is a named variant's */ + (void) value; + { /* per_team */ + int rides = 0; int32_t i; + for ( i = 0; i < TEAM_MAX; i++ ) { - if ( value->per_team[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_per_team++; + if ( value->per_team[i] == 0 ) { continue; } + rides = 1; break; } - if ( pairs_per_team > 0 ) + if ( rides ) { - int64_t len_at_per_team; - /* KIND 16, not 14: a keyed body and a positional one are - incompatible, so a reader of the other kind must see a kind - mismatch and skip, never misdecode (docs/SPEC-TABLES.md §3.2) */ - table_writer_put16( w, 0x443f ); table_writer_put8( w, 16 ); /* per_team (keyed by Team) */ - len_at_per_team = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 4 ); table_writer_put32( w, pairs_per_team ); - /* ASCENDING BY VARIANT ORDINAL, which is slot order — this - writer's choice, and a reader must not rely on it: every - slot is found by its key (docs/SPEC-TABLES.md §3.2) */ - for ( i = 0; i < TEAM_MAX; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xf10fad739a0e1660ull ); table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at_per_team; - if ( value->per_team[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Team) ( i + 1 ) ) /* Team rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TEAM_NONE: key_id = 0; break; - case TEAM_RED: key_id = 0xbb03; break; - case TEAM_BLUE: key_id = 0xf630; break; - case TEAM_GREEN: key_id = 0x6e0f; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - table_writer_put16( w, key_id ); /* the slot's VARIANT id, not its position */ - elem_len_at_per_team = w->offset; table_writer_put32( w, 0 ); - table_writer_put32( w, (uint32_t) ( value->per_team[i] ) ); - table_writer_patch32( w, elem_len_at_per_team, (uint32_t) ( w->offset - elem_len_at_per_team - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_score_board_wire_per_team_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_score_board_wire_per_team_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_score_board_wire_per_team_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_per_team, (uint32_t) ( w->offset - len_at_per_team - 4 ) ); } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t score_board_measure( const ScoreBoard * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !score_board_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t score_board_save( const ScoreBoard * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !score_board_save_body( &w, value ) ) { return -1; } - return w.offset; /* == score_board_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int score_board_load_body( TableReader * r, ScoreBoard * value ) +static SCHEMA_UNUSED int score_board_load_body( TableReader * r, ScoreBoard * value ) { - score_board_reset( value ); /* prefill declared defaults in place, then overlay */ + score_board_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x443f: /* per_team */ + case 0xf10fad739a0e1660ull: /* per_team */ { if ( kind != 16 ) { @@ -1743,92 +3522,316 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int score_board_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - TableReader sub; - uint32_t i; - if ( elem_kind != 4 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); + uint8_t elem_kind; uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 4 ) { if ( !(table_kind_widens(elem_kind,4)) ) { r->report->kind_mismatch++; break; } r->report->widened++; } for ( i = 0; i < count; i++ ) { - uint16_t key; - uint32_t elem_len; - Team slot; - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - key = table_reader_get16( &sub ); - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - if ( key == 0 ) + uint64_t key_ref, key; int32_t slot = -1; TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) { - /* None is the NULL KEY: 0 is the reserved id no declared - name can fold to, so a body carrying one is DAMAGED, not - merely foreign. Framing damage stops this body, keeps what - it decoded, and the parent reads on past the length - (docs/SPEC-TABLES.md §3.2, §4). */ - r->report->malformed = 1; - break; + case 0x9ff1de19feac1b7cull: slot = 0; break; + case 0xecf3d3a7c1693e2dull: slot = 1; break; + case 0xcf00d78fd5953f1cull: slot = 2; break; + default: r->report->unknown++; break; } - slot = TEAM_NONE; + if ( slot < 0 ) { continue; } + switch ( elem_kind ) { - int32_t before = r->report->unknown; - switch ( key ) + case 2: { - case 0: slot = TEAM_NONE; break; - case 0xbb03: slot = TEAM_RED; break; - case 0xf630: slot = TEAM_BLUE; break; - case 0x6e0f: slot = TEAM_GREEN; break; - default: slot = TEAM_NONE; r->report->unknown++; break; /* an id this build cannot name */ + if ( !table_reader_has( &elem, 1 ) ) { r->report->malformed = 1; goto next_per_team; } + int64_t decoded_wide = (int8_t) table_reader_get8( &elem ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100000ll ) { decoded_wide = 100000ll; r->report->clamped++; } + value->per_team[slot] = decoded_wide; + break; } - if ( r->report->unknown != before ) + case 3: { - sub.offset += elem_len; /* a slot this reader cannot name */ - continue; + if ( !table_reader_has( &elem, 2 ) ) { r->report->malformed = 1; goto next_per_team; } + int64_t decoded_wide = (int16_t) table_reader_get16( &elem ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100000ll ) { decoded_wide = 100000ll; r->report->clamped++; } + value->per_team[slot] = decoded_wide; + break; } - } - { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - if ( !table_reader_has( &elem, 4 ) ) { r->report->malformed = 1; sub.offset += elem_len; continue; } + case 4: { - int32_t decoded_v = (int32_t) table_reader_get32( &elem ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 100000 ) { decoded_v = 100000; r->report->clamped++; } - value->per_team[(int32_t) slot - 1] = decoded_v; + if ( !table_reader_has( &elem, 4 ) ) { r->report->malformed = 1; goto next_per_team; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &elem ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 100000 ) { decoded_v = 100000; r->report->clamped++; } + value->per_team[slot] = decoded_v; + } + break; } + default: r->report->malformed = 1; goto next_per_team; } - sub.offset += elem_len; + next_per_team: ; } } - r->offset = body_end; /* unread pairs and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int score_board_load( ScoreBoard * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + score_board_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return score_board_load_body( &r, value ); } +static SCHEMA_UNUSED int score_board_save_message_body(TableBitWriter * w,const ScoreBoard * value) +{ + (void)value; + { /* per_team */ + int rides=0; + { int32_t i; + for(i=0;i<3;i++) { + if(value->per_team[i] == 0) continue; + rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,94,kTableMessageRefBitsHere); + { uint32_t count=0; + int32_t i; + for(i=0;i<3;i++) { + if(value->per_team[i] == 0) continue; + count++; + } + table_bit_put(w,count,2); + for(i=0;i<3;i++) { + if(value->per_team[i] == 0) continue; + switch(i+1) { + case TEAM_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TEAM_RED: table_bit_put(w,122,kTableMessageRefBitsHere); + break; + case TEAM_BLUE: table_bit_put(w,123,kTableMessageRefBitsHere); + break; + case TEAM_GREEN: table_bit_put(w,124,kTableMessageRefBitsHere); + break; + default: return 0; + } + table_bit_put(w,(uint64_t)(value->per_team[i])-UINT64_C(0),17); + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t score_board_measure_messages(const ScoreBoard * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xf10fad739a0e1660): { + if(!(entry->kind==16 && entry->elem_kind==4)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0x9ff1de19feac1b7c):slot=0; + break; + case UINT64_C(0xecf3d3a7c1693e2d):slot=1; + break; + case UINT64_C(0xcf00d78fd5953f1c):slot=2; + break; + default:break; + } if(slot<0){r->report->unknown++; + { int32_t scratch; + memset(&scratch,0,sizeof(scratch)); + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>100000ll){decoded=100000ll; + r->report->clamped++; + } + scratch=(int32_t)decoded; + } + }continue; + } + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>100000ll){decoded=100000ll; + r->report->clamped++; + } + value->per_team[slot]=(int32_t)decoded; + } + } } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int score_board_load_messages(ScoreBoard * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define team_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define team_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define team_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define team_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define team_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define gunner_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define gunner_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define gunner_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define gunner_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define gunner_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define turret_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define turret_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define turret_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define turret_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define turret_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define hull_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define hull_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define hull_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define hull_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define hull_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define keyed_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define keyed_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define keyed_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define keyed_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define keyed_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define score_board_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define score_board_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define score_board_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define score_board_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define score_board_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* team_config_open: match the header and POINT. On a match the bytes ARE what this @@ -1850,6 +3853,8 @@ static SCHEMA_UNUSED int score_board_load( ScoreBoard * value, const uint8_t * b file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const TeamConfig * team_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const TeamConfig *)table_cook_open_ex(bytes,length,sizeof(TeamConfig),SCHEMA_TABLE_ALIGNOF(TeamConfig),reason); } static SCHEMA_UNUSED const TeamConfig * team_config_open( const void * bytes, uint64_t length ) { return (const TeamConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( TeamConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( TeamConfig ) ); @@ -1874,6 +3879,8 @@ static SCHEMA_UNUSED const TeamConfig * team_config_open( const void * bytes, ui file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const GunnerConfig * gunner_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const GunnerConfig *)table_cook_open_ex(bytes,length,sizeof(GunnerConfig),SCHEMA_TABLE_ALIGNOF(GunnerConfig),reason); } static SCHEMA_UNUSED const GunnerConfig * gunner_config_open( const void * bytes, uint64_t length ) { return (const GunnerConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( GunnerConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( GunnerConfig ) ); @@ -1898,6 +3905,8 @@ static SCHEMA_UNUSED const GunnerConfig * gunner_config_open( const void * bytes file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const TurretConfig * turret_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const TurretConfig *)table_cook_open_ex(bytes,length,sizeof(TurretConfig),SCHEMA_TABLE_ALIGNOF(TurretConfig),reason); } static SCHEMA_UNUSED const TurretConfig * turret_config_open( const void * bytes, uint64_t length ) { return (const TurretConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( TurretConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( TurretConfig ) ); @@ -1922,6 +3931,8 @@ static SCHEMA_UNUSED const TurretConfig * turret_config_open( const void * bytes file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const HullConfig * hull_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const HullConfig *)table_cook_open_ex(bytes,length,sizeof(HullConfig),SCHEMA_TABLE_ALIGNOF(HullConfig),reason); } static SCHEMA_UNUSED const HullConfig * hull_config_open( const void * bytes, uint64_t length ) { return (const HullConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( HullConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( HullConfig ) ); @@ -1946,11 +3957,123 @@ static SCHEMA_UNUSED const HullConfig * hull_config_open( const void * bytes, ui file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const KeyedConfig * keyed_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const KeyedConfig *)table_cook_open_ex(bytes,length,sizeof(KeyedConfig),SCHEMA_TABLE_ALIGNOF(KeyedConfig),reason); } static SCHEMA_UNUSED const KeyedConfig * keyed_config_open( const void * bytes, uint64_t length ) { return (const KeyedConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( KeyedConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( KeyedConfig ) ); } +static SCHEMA_UNUSED int schema_tabledemo_team_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TeamConfig * value=(const TeamConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->spawn_count,4,order); + table_cook_bytes(at+4,value->banner,value->banner_length,17); + table_cook_put(at+4+20,(uint32_t)value->banner_length,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t team_config_cook_measure(const TeamConfig * value) { (void)value; return 112; } +static SCHEMA_UNUSED int team_config_cook(const TeamConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<112) { return 0; } + memset(raw,0,112); + if(!schema_tabledemo_team_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+104,UINT64_C(0x1cf8555d11fb113a),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,32,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_gunner_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const GunnerConfig * value=(const GunnerConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + { uint32_t bits; memcpy(&bits,&value->reaction,4); table_cook_put(at+0,bits,4,order); } + table_cook_put(at+4,value->tracking ? 1 : 0,1,order); + return 1; +} +static SCHEMA_UNUSED int64_t gunner_config_cook_measure(const GunnerConfig * value) { (void)value; return 88; } +static SCHEMA_UNUSED int gunner_config_cook(const GunnerConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<88) { return 0; } + memset(raw,0,88); + if(!schema_tabledemo_gunner_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+80,UINT64_C(0x5fcbe04615411b64),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,8,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_turret_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TurretConfig * value=(const TurretConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + { uint32_t bits; memcpy(&bits,&value->damage,4); table_cook_put(at+0,bits,4,order); } + { uint32_t bits; memcpy(&bits,&value->cooldown,4); table_cook_put(at+4,bits,4,order); } + table_cook_put(at+8+8,value->gunner_present ? 1 : 0,1,order); + if(!schema_tabledemo_gunner_config_cook_body_(n,at+8,&value->gunner,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t turret_config_cook_measure(const TurretConfig * value) { (void)value; return 104; } +static SCHEMA_UNUSED int turret_config_cook(const TurretConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<104) { return 0; } + memset(raw,0,104); + if(!schema_tabledemo_turret_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+96,UINT64_C(0x469dba0c16b2ad15),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,24,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_hull_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const HullConfig * value=(const HullConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + { uint32_t bits; memcpy(&bits,&value->health,4); table_cook_put(at+0,bits,4,order); } + { uint32_t bits; memcpy(&bits,&value->mass,4); table_cook_put(at+4,bits,4,order); } + { int32_t cook_i1; for(cook_i1=0;cook_i1<3;cook_i1++) { + if(!schema_tabledemo_turret_config_cook_body_(n,at+8+cook_i1*20,&value->turrets[cook_i1],order)) { return 0; } + } } + return 1; +} +static SCHEMA_UNUSED int64_t hull_config_cook_measure(const HullConfig * value) { (void)value; return 152; } +static SCHEMA_UNUSED int hull_config_cook(const HullConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<152) { return 0; } + memset(raw,0,152); + if(!schema_tabledemo_hull_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+144,UINT64_C(0x3066b130dfbd7890),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,72,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_keyed_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const KeyedConfig * value=(const KeyedConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + { int32_t cook_i1; for(cook_i1=0;cook_i1<3;cook_i1++) { + if(!schema_tabledemo_team_config_cook_body_(n,at+0+cook_i1*28,&value->teams[cook_i1],order)) { return 0; } + } } + { int32_t cook_i2; for(cook_i2=0;cook_i2<3;cook_i2++) { + if(!schema_tabledemo_hull_config_cook_body_(n,at+84+cook_i2*68,&value->hulls[cook_i2],order)) { return 0; } + } } + if(!schema_tabledemo_score_board_cook_body_(n,at+288,&value->scores,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t keyed_config_cook_measure(const KeyedConfig * value) { (void)value; return 384; } +static SCHEMA_UNUSED int keyed_config_cook(const KeyedConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<384) { return 0; } + memset(raw,0,384); + if(!schema_tabledemo_keyed_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+376,UINT64_C(0xd6633ae4e94deecf),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,304,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_score_board_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const ScoreBoard * value=(const ScoreBoard *)storage; + (void)n; (void)at; (void)value; (void)order; + { int32_t cook_i1; for(cook_i1=0;cook_i1<3;cook_i1++) { + table_cook_put(at+0+cook_i1*4,(uint64_t)value->per_team[cook_i1],4,order); + } } + return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/examples-c/NestedTable.c b/testdata/golden/tables/examples-c/NestedTable.c index d31b0a55b..c92ad938e 100644 --- a/testdata/golden/tables/examples-c/NestedTable.c +++ b/testdata/golden/tables/examples-c/NestedTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) - { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,8 +2330,8 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableFieldInfo schema_tabledemo_archive_config_fields_[] = { - { "root", "root", "RootConfig", 0x2eb8, 13, 0, 0, 0, 0, (uint32_t) offsetof( ArchiveConfig, root ), (uint32_t) sizeof( ( (ArchiveConfig *) 0 )->root ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_root_config_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "count", "count", "int32", 0xe445, 4, 0, 0, 0, 0, (uint32_t) offsetof( ArchiveConfig, count ), (uint32_t) sizeof( ( (ArchiveConfig *) 0 )->count ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "root", "root", "RootConfig", 0xa354fd1ff0c467c5ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ArchiveConfig, root ), (uint32_t) sizeof( ( (ArchiveConfig *) 0 )->root ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_root_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "count", "count", "int32", 0xb1e5e28e4479a274ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( ArchiveConfig, count ), (uint32_t) sizeof( ( (ArchiveConfig *) 0 )->count ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_archive_config_info_ = { "ArchiveConfig", (uint32_t) sizeof( ArchiveConfig ), 2, schema_tabledemo_archive_config_fields_, schema_tabledemo_archive_config_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/examples-c/NestedTable.h b/testdata/golden/tables/examples-c/NestedTable.h index 37a2187c0..fca02f688 100644 --- a/testdata/golden/tables/examples-c/NestedTable.h +++ b/testdata/golden/tables/examples-c/NestedTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package tabledemo — protocol id 0xba7b344f28584d33 (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_TABLEDEMO_NESTEDTABLE_H #define SCHEMA_TABLEDEMO_NESTEDTABLE_H @@ -110,8 +110,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -120,6 +126,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -129,6 +136,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -155,7 +164,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -165,12 +174,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -184,6 +195,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -211,6 +224,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -232,157 +247,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[156]; + uint32_t slots[512], positions[156]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (512-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(512-1); + } + if(v->count==156) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } } - return 0; + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -392,6 +527,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_TABLEDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_TABLEDEMO_BUILD_VERSION #define SCHEMA_TABLEDEMO_BUILD_VERSION @@ -503,25 +693,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -531,40 +721,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_TABLEDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table ArchiveConfig — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in archive_config_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -589,64 +1360,84 @@ static SCHEMA_UNUSED void schema_tabledemo_archive_config_reset_raw_( void * sto /* ---- codecs: measure/save/load per closure member ---- */ static SCHEMA_UNUSED int64_t archive_config_measure( const ArchiveConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int archive_config_save_body( TableWriter * w, const ArchiveConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int archive_config_load_body( TableReader * r, ArchiveConfig * value ); - -static SCHEMA_UNUSED int64_t archive_config_measure( const ArchiveConfig * value ) -{ - int64_t bytes = 2; /* terminator */ - { - int64_t body_root = root_config_measure( &value->root ); - if ( body_root < 0 ) { return -1; } - if ( body_root > 2 ) { bytes += 3 + 4 + body_root; } /* root: all-default nested elides */ - } - if ( value->count != 1 ) { bytes += 3 + 4; } /* count */ - return bytes; -} - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int archive_config_save_body( TableWriter * w, const ArchiveConfig * value ) +static SCHEMA_UNUSED int archive_config_save_body( TableWriter * w, const ArchiveConfig * value ); +static SCHEMA_UNUSED int archive_config_load_body( TableReader * r, ArchiveConfig * value ); + +static SCHEMA_UNUSED int schema_tabledemo_archive_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int archive_config_save_message_body(TableBitWriter *,const ArchiveConfig *); +static SCHEMA_UNUSED int archive_config_load_message_body(TableMessageReader *,ArchiveConfig *); +static SCHEMA_UNUSED int schema_tabledemo_archive_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int archive_config_save_body( TableWriter * w, const ArchiveConfig * value ) { - { - int64_t body_root = root_config_measure( &value->root ); - if ( body_root < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_root > 2 ) /* all-default nested elides */ + (void) value; + { /* root */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !root_config_save_body( &default_probe, &value->root ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0x2eb8 ); table_writer_put8( w, 13 ); /* root */ - table_writer_put32( w, (uint32_t) body_root ); - if ( !root_config_save_body( w, &value->root ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa354fd1ff0c467c5ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !root_config_save_body( w, &value->root ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !root_config_save_body( &probe, &value->root ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !root_config_save_body( w, &value->root ) ) { return 0; } + } } } - if ( value->count != 1 ) - { - table_writer_put16( w, 0xe445 ); table_writer_put8( w, 4 ); /* count */ - table_writer_put32( w, (uint32_t) ( value->count ) ); + { /* count */ + if ( !( value->count == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb1e5e28e4479a274ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->count ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t archive_config_measure( const ArchiveConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !archive_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t archive_config_save( const ArchiveConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !archive_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == archive_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int archive_config_load_body( TableReader * r, ArchiveConfig * value ) +static SCHEMA_UNUSED int archive_config_load_body( TableReader * r, ArchiveConfig * value ) { - archive_config_reset( value ); /* prefill declared defaults in place, then overlay */ + archive_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x2eb8: /* root */ + case 0xa354fd1ff0c467c5ull: /* root */ { if ( kind != 13 ) { @@ -654,53 +1445,257 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int archive_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - root_config_load_body( &sub, &value->root ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + root_config_load_body( &sub, &value->root ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; root_config_reset( &value->root ); } break; } - case 0xe445: /* count */ + case 0xb1e5e28e4479a274ull: /* count */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100ll ) { decoded_wide = 100ll; r->report->clamped++; } + value->count = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100ll ) { decoded_wide = 100ll; r->report->clamped++; } + value->count = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 100 ) { decoded_v = 100; r->report->clamped++; } + value->count = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 100 ) { decoded_v = 100; r->report->clamped++; } - value->count = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100ll ) { decoded_wide = 100ll; r->report->clamped++; } + value->count = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100ll ) { decoded_wide = 100ll; r->report->clamped++; } + value->count = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 100 ) { decoded_v = 100; r->report->clamped++; } + value->count = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int archive_config_load( ArchiveConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + archive_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return archive_config_load_body( &r, value ); } +static SCHEMA_UNUSED int archive_config_save_message_body(TableBitWriter * w,const ArchiveConfig * value) +{ + (void)value; + { /* root */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!root_config_save_message_body(&probe,&value->root)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,1,kTableMessageRefBitsHere); + if(!root_config_save_message_body(w,&value->root)) return 0; + } + } + { /* count */ + if(!(value->count == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,2,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->count)-UINT64_C(0),7); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t archive_config_measure_messages(const ArchiveConfig * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xa354fd1ff0c467c5): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!root_config_load_message_body(r,&value->root))return 0; + break; + } + case UINT64_C(0xb1e5e28e4479a274): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>100ll){decoded=100ll; + r->report->clamped++; + } + value->count=(int32_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int archive_config_load_messages(ArchiveConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define archive_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define archive_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define archive_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define archive_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define archive_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* archive_config_open: match the header and POINT. On a match the bytes ARE what this @@ -722,11 +1717,31 @@ static SCHEMA_UNUSED int archive_config_load( ArchiveConfig * value, const uint8 file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const ArchiveConfig * archive_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const ArchiveConfig *)table_cook_open_ex(bytes,length,sizeof(ArchiveConfig),SCHEMA_TABLE_ALIGNOF(ArchiveConfig),reason); } static SCHEMA_UNUSED const ArchiveConfig * archive_config_open( const void * bytes, uint64_t length ) { return (const ArchiveConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( ArchiveConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( ArchiveConfig ) ); } +static SCHEMA_UNUSED int schema_tabledemo_archive_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const ArchiveConfig * value=(const ArchiveConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + if(!schema_tabledemo_root_config_cook_body_(n,at+0,&value->root,order)) { return 0; } + table_cook_put(at+1480,(uint64_t)value->count,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t archive_config_cook_measure(const ArchiveConfig * value) { (void)value; return 1568; } +static SCHEMA_UNUSED int archive_config_cook(const ArchiveConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<1568) { return 0; } + memset(raw,0,1568); + if(!schema_tabledemo_archive_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+1560,UINT64_C(0x413e7bcf261bc3c7),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,1488,1,8,order); return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/examples-c/PackTable.c b/testdata/golden/tables/examples-c/PackTable.c index 1fa0cfa4f..953151db5 100644 --- a/testdata/golden/tables/examples-c/PackTable.c +++ b/testdata/golden/tables/examples-c/PackTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) - { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,56 +2330,56 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableFieldInfo schema_tabledemo_gunner_settings_fields_[] = { - { "reaction", "reaction", "float32", 0x900f, 10, 0, 0, 0, 0, (uint32_t) offsetof( GunnerSettings, reaction ), (uint32_t) sizeof( ( (GunnerSettings *) 0 )->reaction ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "tracking", "tracking", "bool", 0x53d5, 1, 0, 0, 0, 0, (uint32_t) offsetof( GunnerSettings, tracking ), (uint32_t) sizeof( ( (GunnerSettings *) 0 )->tracking ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "callsign", "callsign", "string", 0x74dc, 12, 0, 1, 0, 24, (uint32_t) offsetof( GunnerSettings, callsign ), (uint32_t) sizeof( ( (GunnerSettings *) 0 )->callsign ), (uint32_t) offsetof( GunnerSettings, callsign_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "reaction", "reaction", "float32", 0xb75aa3662201646aull, 10, 0, 0, 0, 0, (uint32_t) offsetof( GunnerSettings, reaction ), (uint32_t) sizeof( ( (GunnerSettings *) 0 )->reaction ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tracking", "tracking", "bool", 0xa6bf719a4602b0bcull, 1, 0, 0, 0, 0, (uint32_t) offsetof( GunnerSettings, tracking ), (uint32_t) sizeof( ( (GunnerSettings *) 0 )->tracking ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "callsign", "callsign", "string", 0x5bc44627b9848818ull, 12, 0, 1, 0, 24, (uint32_t) offsetof( GunnerSettings, callsign ), (uint32_t) sizeof( ( (GunnerSettings *) 0 )->callsign ), (uint32_t) offsetof( GunnerSettings, callsign_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_gunner_settings_info_ = { "GunnerSettings", (uint32_t) sizeof( GunnerSettings ), 3, schema_tabledemo_gunner_settings_fields_, schema_tabledemo_gunner_settings_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_ship_entry_fields_[] = { - { "display_name", "name", "string", 0xcb8b, 12, 0, 1, 0, 32, (uint32_t) offsetof( ShipEntry, display_name ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->display_name ), (uint32_t) offsetof( ShipEntry, display_name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "health", "health", "float32", 0x8617, 10, 0, 0, 0, 0, (uint32_t) offsetof( ShipEntry, health ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "mass", "mass", "float32", 0xe7a6, 10, 0, 0, 0, 0, (uint32_t) offsetof( ShipEntry, mass ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->mass ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "hardpoints", "hardpoints", "int32", 0xc4b2, 4, 1, 1, 0, 4, (uint32_t) offsetof( ShipEntry, hardpoints ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->hardpoints[0] ), (uint32_t) offsetof( ShipEntry, hardpoints_count ), 0xffffffffu, NULL, 1, 0.0, 8.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "gunner", "gunner", "GunnerSettings", 0x2bc9, 13, 0, 0, 1, 0, (uint32_t) offsetof( ShipEntry, gunner ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->gunner ), 0xffffffffu, (uint32_t) offsetof( ShipEntry, gunner_present ), &schema_tabledemo_gunner_settings_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "display_name", "name", "string", 0x2d21d7cd66bd5a5dull, 12, 0, 1, 0, 32, (uint32_t) offsetof( ShipEntry, display_name ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->display_name ), (uint32_t) offsetof( ShipEntry, display_name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "health", "health", "float32", 0x7f69d4b5288ba9cfull, 10, 0, 0, 0, 0, (uint32_t) offsetof( ShipEntry, health ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->health ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "mass", "mass", "float32", 0x1f3757a2ce7b0ab1ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( ShipEntry, mass ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->mass ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "hardpoints", "hardpoints", "int32", 0x95d0cce09ca82b73ull, 4, 1, 1, 0, 4, (uint32_t) offsetof( ShipEntry, hardpoints ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->hardpoints[0] ), (uint32_t) offsetof( ShipEntry, hardpoints_count ), 0xffffffffu, NULL, 1, 0.0, 8.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "gunner", "gunner", "GunnerSettings", 0x40dbb648c0cd44aaull, 13, 0, 0, 1, 0, (uint32_t) offsetof( ShipEntry, gunner ), (uint32_t) sizeof( ( (ShipEntry *) 0 )->gunner ), 0xffffffffu, (uint32_t) offsetof( ShipEntry, gunner_present ), &schema_tabledemo_gunner_settings_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_ship_entry_info_ = { "ShipEntry", (uint32_t) sizeof( ShipEntry ), 5, schema_tabledemo_ship_entry_fields_, schema_tabledemo_ship_entry_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_tabledemo_global_settings_difficulty_variants_[] = { - { "None", 0x0000 }, - { "Easy", 0x7d08 }, - { "Normal", 0x7fac }, - { "Hard", 0xc313 }, + { "None", 0x0000000000000000ull }, + { "Easy", 0x483d9e6c1ccd1f9bull }, + { "Normal", 0x9ff3121d30f88d52ull }, + { "Hard", 0x58c7b5d87587287cull }, }; static const TableFieldInfo schema_tabledemo_global_settings_fields_[] = { - { "tick_rate", "tick_rate", "uint32", 0x1953, 8, 0, 0, 0, 0, (uint32_t) offsetof( GlobalSettings, tick_rate ), (uint32_t) sizeof( ( (GlobalSettings *) 0 )->tick_rate ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 240.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "difficulty", "difficulty", "Difficulty", 0xd53f, 7, 0, 0, 0, 0, (uint32_t) offsetof( GlobalSettings, difficulty ), (uint32_t) sizeof( ( (GlobalSettings *) 0 )->difficulty ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_tabledemo_global_settings_difficulty_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "build_note", "build_note", "string", 0xb2d4, 12, 0, 1, 0, 48, (uint32_t) offsetof( GlobalSettings, build_note ), (uint32_t) sizeof( ( (GlobalSettings *) 0 )->build_note ), (uint32_t) offsetof( GlobalSettings, build_note_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "spawn_delays", "spawn_delays", "float32", 0x7c01, 10, 1, 0, 0, 3, (uint32_t) offsetof( GlobalSettings, spawn_delays ), (uint32_t) sizeof( ( (GlobalSettings *) 0 )->spawn_delays[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "tick_rate", "tick_rate", "uint32", 0xa65f859b617deaf3ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( GlobalSettings, tick_rate ), (uint32_t) sizeof( ( (GlobalSettings *) 0 )->tick_rate ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 240.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "difficulty", "difficulty", "Difficulty", 0x467f35a74c6e4a70ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( GlobalSettings, difficulty ), (uint32_t) sizeof( ( (GlobalSettings *) 0 )->difficulty ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_tabledemo_global_settings_difficulty_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "build_note", "build_note", "string", 0x14ec921cc2549d60ull, 12, 0, 1, 0, 48, (uint32_t) offsetof( GlobalSettings, build_note ), (uint32_t) sizeof( ( (GlobalSettings *) 0 )->build_note ), (uint32_t) offsetof( GlobalSettings, build_note_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "spawn_delays", "spawn_delays", "float32", 0x09ae5613b0051271ull, 10, 1, 0, 0, 3, (uint32_t) offsetof( GlobalSettings, spawn_delays ), (uint32_t) sizeof( ( (GlobalSettings *) 0 )->spawn_delays[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_global_settings_info_ = { "GlobalSettings", (uint32_t) sizeof( GlobalSettings ), 4, schema_tabledemo_global_settings_fields_, schema_tabledemo_global_settings_reset_raw_, TableDocNone, 0, NULL }; static const TableVariantInfo schema_tabledemo_pack_config_ships_keys_[] = { - { "None", 0x0000 }, - { "Fighter", 0x412b }, - { "Bomber", 0xb7ce }, - { "Scout", 0xd97a }, + { "None", 0x0000000000000000ull }, + { "Fighter", 0xd011f7c3c15285c2ull }, + { "Bomber", 0xa8216aa1c554cb8aull }, + { "Scout", 0x7d573c625719c1a5ull }, }; static const TableVariantInfo schema_tabledemo_pack_config_thresholds_keys_[] = { - { "None", 0x0000 }, - { "Easy", 0x7d08 }, - { "Normal", 0x7fac }, - { "Hard", 0xc313 }, + { "None", 0x0000000000000000ull }, + { "Easy", 0x483d9e6c1ccd1f9bull }, + { "Normal", 0x9ff3121d30f88d52ull }, + { "Hard", 0x58c7b5d87587287cull }, }; static const TableFieldInfo schema_tabledemo_pack_config_fields_[] = { - { "version", "version", "uint32", 0xe8e6, 8, 0, 0, 0, 0, (uint32_t) offsetof( PackConfig, version ), (uint32_t) sizeof( ( (PackConfig *) 0 )->version ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "global", "global", "GlobalSettings", 0x1b51, 13, 0, 0, 0, 0, (uint32_t) offsetof( PackConfig, global ), (uint32_t) sizeof( ( (PackConfig *) 0 )->global ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_global_settings_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "ships", "ships", "ShipEntry", 0x2d39, 13, 1, 0, 0, (int32_t) SHIP_TYPE_MAX, (uint32_t) offsetof( PackConfig, ships ), (uint32_t) sizeof( ( (PackConfig *) 0 )->ships[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_ship_entry_info_, 0, 0.0, 0.0, -1, NULL, 0, "ShipType", schema_tabledemo_pack_config_ships_keys_, 3, NULL, "", TableDocNone, 0, NULL }, - { "thresholds", "thresholds", "int32", 0xb2eb, 4, 1, 0, 0, (int32_t) DIFFICULTY_MAX, (uint32_t) offsetof( PackConfig, thresholds ), (uint32_t) sizeof( ( (PackConfig *) 0 )->thresholds[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, -1, NULL, 0, "Difficulty", schema_tabledemo_pack_config_thresholds_keys_, 3, NULL, "", TableDocNone, 0, NULL }, - { "reserves", "reserves", "ShipEntry", 0x049d, 13, 1, 1, 0, 3, (uint32_t) offsetof( PackConfig, reserves ), (uint32_t) sizeof( ( (PackConfig *) 0 )->reserves[0] ), (uint32_t) offsetof( PackConfig, reserves_count ), 0xffffffffu, &schema_tabledemo_ship_entry_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "version", "version", "uint32", 0xbb62c62c9808ea37ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( PackConfig, version ), (uint32_t) sizeof( ( (PackConfig *) 0 )->version ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "global", "global", "GlobalSettings", 0x7fb43557b54149ceull, 13, 0, 0, 0, 0, (uint32_t) offsetof( PackConfig, global ), (uint32_t) sizeof( ( (PackConfig *) 0 )->global ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_global_settings_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ships", "ships", "ShipEntry", 0x294a5c4913e1ad44ull, 13, 1, 0, 0, (int32_t) SHIP_TYPE_MAX, (uint32_t) offsetof( PackConfig, ships ), (uint32_t) sizeof( ( (PackConfig *) 0 )->ships[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_ship_entry_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, "ShipType", schema_tabledemo_pack_config_ships_keys_, 3, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "thresholds", "thresholds", "int32", 0x9cda940a344e1571ull, 4, 1, 0, 0, (int32_t) DIFFICULTY_MAX, (uint32_t) offsetof( PackConfig, thresholds ), (uint32_t) sizeof( ( (PackConfig *) 0 )->thresholds[0] ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, "Difficulty", schema_tabledemo_pack_config_thresholds_keys_, 3, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "reserves", "reserves", "ShipEntry", 0x77707fccd201c228ull, 13, 1, 1, 0, 3, (uint32_t) offsetof( PackConfig, reserves ), (uint32_t) sizeof( ( (PackConfig *) 0 )->reserves[0] ), (uint32_t) offsetof( PackConfig, reserves_count ), 0xffffffffu, &schema_tabledemo_ship_entry_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_pack_config_info_ = { "PackConfig", (uint32_t) sizeof( PackConfig ), 5, schema_tabledemo_pack_config_fields_, schema_tabledemo_pack_config_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/examples-c/PackTable.h b/testdata/golden/tables/examples-c/PackTable.h index 91f1c7106..f6a92e485 100644 --- a/testdata/golden/tables/examples-c/PackTable.h +++ b/testdata/golden/tables/examples-c/PackTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package tabledemo — protocol id 0xba7b344f28584d33 (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_TABLEDEMO_PACKTABLE_H #define SCHEMA_TABLEDEMO_PACKTABLE_H @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -183,6 +194,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -210,6 +223,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -231,157 +246,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[156]; + uint32_t slots[512], positions[156]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (512-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(512-1); + } + if(v->count==156) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } } - return 0; + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -391,6 +526,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_TABLEDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_TABLEDEMO_BUILD_VERSION #define SCHEMA_TABLEDEMO_BUILD_VERSION @@ -502,25 +692,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -530,40 +720,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_TABLEDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table GunnerSettings — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in gunner_settings_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -677,70 +1448,115 @@ static SCHEMA_UNUSED int64_t gunner_settings_measure( const GunnerSettings * val static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_settings_save_body( TableWriter * w, const GunnerSettings * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_settings_load_body( TableReader * r, GunnerSettings * value ); static SCHEMA_UNUSED int64_t ship_entry_measure( const ShipEntry * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_save_body( TableWriter * w, const ShipEntry * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_load_body( TableReader * r, ShipEntry * value ); +static SCHEMA_UNUSED int ship_entry_save_body( TableWriter * w, const ShipEntry * value ); +static SCHEMA_UNUSED int ship_entry_load_body( TableReader * r, ShipEntry * value ); static SCHEMA_UNUSED int64_t global_settings_measure( const GlobalSettings * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int global_settings_save_body( TableWriter * w, const GlobalSettings * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int global_settings_load_body( TableReader * r, GlobalSettings * value ); static SCHEMA_UNUSED int64_t pack_config_measure( const PackConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int pack_config_save_body( TableWriter * w, const PackConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int pack_config_load_body( TableReader * r, PackConfig * value ); - -static SCHEMA_UNUSED int64_t gunner_settings_measure( const GunnerSettings * value ) +static SCHEMA_UNUSED int pack_config_save_body( TableWriter * w, const PackConfig * value ); +static SCHEMA_UNUSED int pack_config_load_body( TableReader * r, PackConfig * value ); + +static SCHEMA_UNUSED int schema_tabledemo_gunner_settings_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_ship_entry_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_global_settings_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_pack_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int gunner_settings_save_message_body(TableBitWriter *,const GunnerSettings *); +static SCHEMA_UNUSED int ship_entry_save_message_body(TableBitWriter *,const ShipEntry *); +static SCHEMA_UNUSED int global_settings_save_message_body(TableBitWriter *,const GlobalSettings *); +static SCHEMA_UNUSED int pack_config_save_message_body(TableBitWriter *,const PackConfig *); +static SCHEMA_UNUSED int gunner_settings_load_message_body(TableMessageReader *,GunnerSettings *); +static SCHEMA_UNUSED int ship_entry_load_message_body(TableMessageReader *,ShipEntry *); +static SCHEMA_UNUSED int global_settings_load_message_body(TableMessageReader *,GlobalSettings *); +static SCHEMA_UNUSED int pack_config_load_message_body(TableMessageReader *,PackConfig *); +static SCHEMA_UNUSED int schema_tabledemo_gunner_settings_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_ship_entry_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_global_settings_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_pack_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_gunner_settings_wire_callsign_( TableWriter * w, const GunnerSettings * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->reaction != 0.2f ) { bytes += 3 + 4; } /* reaction */ - if ( value->tracking != 0 ) { bytes += 3 + 1; } /* tracking */ - if ( value->callsign_length < 0 || value->callsign_length > 24 ) { return -1; } /* storage invariant */ - if ( value->callsign_length > 0 ) { bytes += 3 + 4 + value->callsign_length; } /* callsign */ - return bytes; + if ( value->callsign_length < 0 || value->callsign_length > 24 ) { return 0; } + table_writer_raw( w, value->callsign, value->callsign_length ); + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_settings_save_body( TableWriter * w, const GunnerSettings * value ) +static SCHEMA_UNUSED int gunner_settings_save_body( TableWriter * w, const GunnerSettings * value ) { - if ( value->reaction != 0.2f ) - { - table_writer_put16( w, 0x900f ); table_writer_put8( w, 10 ); /* reaction */ - table_writer_put32( w, table_float_to_bits( value->reaction ) ); + (void) value; + { /* reaction */ + if ( !( value->reaction == 0.2f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb75aa3662201646aull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->reaction ) ); + } } - if ( value->tracking != 0 ) - { - table_writer_put16( w, 0x53d5 ); table_writer_put8( w, 1 ); /* tracking */ - table_writer_put8( w, value->tracking ? 1 : 0 ); + { /* tracking */ + if ( !( value->tracking == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa6bf719a4602b0bcull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->tracking ? 1 : 0 ); + } } - if ( value->callsign_length < 0 || value->callsign_length > 24 ) { return 0; } /* storage invariant */ - if ( value->callsign_length > 0 ) - { - table_writer_put16( w, 0x74dc ); table_writer_put8( w, 12 ); /* callsign */ - table_writer_put32( w, (uint32_t) value->callsign_length ); - table_writer_raw( w, value->callsign, value->callsign_length ); + { /* callsign */ + if ( value->callsign_length < 0 || value->callsign_length > 24 ) { return 0; } + if ( value->callsign_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5bc44627b9848818ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_gunner_settings_wire_callsign_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_gunner_settings_wire_callsign_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_gunner_settings_wire_callsign_( w, value ) ) { return 0; } + } + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t gunner_settings_measure( const GunnerSettings * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !gunner_settings_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t gunner_settings_save( const GunnerSettings * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !gunner_settings_save_body( &w, value ) ) { return -1; } - return w.offset; /* == gunner_settings_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_settings_load_body( TableReader * r, GunnerSettings * value ) +static SCHEMA_UNUSED int gunner_settings_load_body( TableReader * r, GunnerSettings * value ) { - gunner_settings_reset( value ); /* prefill declared defaults in place, then overlay */ + gunner_settings_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x900f: /* reaction */ + case 0xb75aa3662201646aull: /* reaction */ { if ( kind != 10 ) { @@ -748,11 +1564,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_settings_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->reaction = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->reaction = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x53d5: /* tracking */ + case 0xa6bf719a4602b0bcull: /* tracking */ { if ( kind != 1 ) { @@ -760,11 +1584,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_settings_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->tracking = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->tracking = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x74dc: /* callsign */ + case 0x5bc44627b9848818ull: /* callsign */ { if ( kind != 12 ) { @@ -772,126 +1604,338 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int gunner_settings_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 24 ) { keep = 24; r->report->clamped++; } - memcpy( value->callsign, r->buffer + r->offset, keep ); - value->callsign[keep] = 0; - value->callsign_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->callsign[0] = 0; value->callsign_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 24 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 24 ); r->report->clamped++; } + memcpy( value->callsign, sub.buffer, (size_t) keep ); value->callsign[keep] = 0; value->callsign_length = (int32_t) keep; break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int gunner_settings_load( GunnerSettings * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + gunner_settings_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return gunner_settings_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t ship_entry_measure( const ShipEntry * value ) +static SCHEMA_UNUSED int gunner_settings_save_message_body(TableBitWriter * w,const GunnerSettings * value) { - int64_t bytes = 2; /* terminator */ - if ( value->display_name_length < 0 || value->display_name_length > 32 ) { return -1; } /* storage invariant */ - if ( value->display_name_length > 0 ) { bytes += 3 + 4 + value->display_name_length; } /* display_name */ - if ( value->health != 100.0f ) { bytes += 3 + 4; } /* health */ - if ( value->mass != 1.0f ) { bytes += 3 + 4; } /* mass */ - if ( value->hardpoints_count < 0 || value->hardpoints_count > 4 ) { return -1; } /* storage invariant */ - if ( value->hardpoints_count > 0 ) - { - bytes += 3 + 4 + 5 + (int64_t) value->hardpoints_count * 4; /* hardpoints */ - } - if ( value->gunner_present ) /* ?GunnerSettings: presence decides, not content */ + (void)value; + { /* reaction */ + if(!(value->reaction == 0.2f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,11,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->reaction),32); + } + } + { /* tracking */ + if(!(value->tracking == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,12,kTableMessageRefBitsHere); + table_bit_put(w,value->tracking ? 1 : 0,1); + } + } + { /* callsign */ + if(value->callsign_length<0 || value->callsign_length>24) return 0; + if(value->callsign_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + if(value->callsign_length<0 || value->callsign_length>24) return 0; + table_bit_put(w,(uint64_t)value->callsign_length,5); + table_bit_align_write(w); + table_bit_putbytes(w,value->callsign,value->callsign_length); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t gunner_settings_measure_messages(const GunnerSettings * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xb75aa3662201646a): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->reaction=decoded; + } + break; + } + case UINT64_C(0xa6bf719a4602b0bc): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->tracking=lo!=0; + } + break; + } + case UINT64_C(0x5bc44627b9848818): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,24); + memcpy(value->callsign,text,(size_t)kept); + value->callsign[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->callsign_length=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int gunner_settings_load_messages(GunnerSettings * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_tabledemo_ship_entry_wire_display_name_( TableWriter * w, const ShipEntry * value ) +{ + if ( value->display_name_length < 0 || value->display_name_length > 32 ) { return 0; } + table_writer_raw( w, value->display_name, value->display_name_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_ship_entry_wire_hardpoints_( TableWriter * w, const ShipEntry * value ) +{ + if ( value->hardpoints_count < 0 || value->hardpoints_count > 4 ) { return 0; } + int32_t i; + table_writer_put8( w, 4 ); table_writer_leb( w, (uint64_t) value->hardpoints_count ); + for ( i = 0; i < value->hardpoints_count; i++ ) { - int64_t body_gunner = gunner_settings_measure( &value->gunner ); - if ( body_gunner < 0 ) { return -1; } - bytes += 3 + 4 + body_gunner; /* gunner */ + table_writer_put32( w, (uint32_t) ( value->hardpoints[i] ) ); } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_save_body( TableWriter * w, const ShipEntry * value ) +static SCHEMA_UNUSED int ship_entry_save_body( TableWriter * w, const ShipEntry * value ) { - if ( value->display_name_length < 0 || value->display_name_length > 32 ) { return 0; } /* storage invariant */ - if ( value->display_name_length > 0 ) - { - table_writer_put16( w, 0xcb8b ); table_writer_put8( w, 12 ); /* display_name */ - table_writer_put32( w, (uint32_t) value->display_name_length ); - table_writer_raw( w, value->display_name, value->display_name_length ); + (void) value; + { /* display_name */ + if ( value->display_name_length < 0 || value->display_name_length > 32 ) { return 0; } + if ( value->display_name_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x2d21d7cd66bd5a5dull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_ship_entry_wire_display_name_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_ship_entry_wire_display_name_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_ship_entry_wire_display_name_( w, value ) ) { return 0; } + } + } } - if ( value->health != 100.0f ) - { - table_writer_put16( w, 0x8617 ); table_writer_put8( w, 10 ); /* health */ - table_writer_put32( w, table_float_to_bits( value->health ) ); + { /* health */ + if ( !( value->health == 100.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7f69d4b5288ba9cfull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->health ) ); + } } - if ( value->mass != 1.0f ) - { - table_writer_put16( w, 0xe7a6 ); table_writer_put8( w, 10 ); /* mass */ - table_writer_put32( w, table_float_to_bits( value->mass ) ); + { /* mass */ + if ( !( value->mass == 1.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1f3757a2ce7b0ab1ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->mass ) ); + } } - if ( value->hardpoints_count < 0 || value->hardpoints_count > 4 ) { return 0; } /* storage invariant */ - if ( value->hardpoints_count > 0 ) - { - int64_t len_at_hardpoints; - int32_t i; - table_writer_put16( w, 0xc4b2 ); table_writer_put8( w, 14 ); /* hardpoints */ - len_at_hardpoints = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 4 ); table_writer_put32( w, (uint32_t) value->hardpoints_count ); - for ( i = 0; i < value->hardpoints_count; i++ ) + { /* hardpoints */ + if ( value->hardpoints_count < 0 || value->hardpoints_count > 4 ) { return 0; } + if ( value->hardpoints_count > 0 ) { - table_writer_put32( w, (uint32_t) ( value->hardpoints[i] ) ); + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x95d0cce09ca82b73ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_ship_entry_wire_hardpoints_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_ship_entry_wire_hardpoints_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_ship_entry_wire_hardpoints_( w, value ) ) { return 0; } + } } - table_writer_patch32( w, len_at_hardpoints, (uint32_t) ( w->offset - len_at_hardpoints - 4 ) ); } - if ( value->gunner_present ) /* ?GunnerSettings */ - { - int64_t body_gunner = gunner_settings_measure( &value->gunner ); - if ( body_gunner < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - table_writer_put16( w, 0x2bc9 ); table_writer_put8( w, 13 ); /* gunner */ - table_writer_put32( w, (uint32_t) body_gunner ); - if ( !gunner_settings_save_body( w, &value->gunner ) ) { return 0; } + { /* gunner */ + if ( value->gunner_present ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x40dbb648c0cd44aaull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !gunner_settings_save_body( w, &value->gunner ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !gunner_settings_save_body( &probe, &value->gunner ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !gunner_settings_save_body( w, &value->gunner ) ) { return 0; } + } + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t ship_entry_measure( const ShipEntry * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !ship_entry_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t ship_entry_save( const ShipEntry * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !ship_entry_save_body( &w, value ) ) { return -1; } - return w.offset; /* == ship_entry_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_load_body( TableReader * r, ShipEntry * value ) +static SCHEMA_UNUSED int ship_entry_load_body( TableReader * r, ShipEntry * value ) { - ship_entry_reset( value ); /* prefill declared defaults in place, then overlay */ + ship_entry_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xcb8b: /* display_name */ + case 0x2d21d7cd66bd5a5dull: /* display_name */ { if ( kind != 12 ) { @@ -899,20 +1943,18 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 32 ) { keep = 32; r->report->clamped++; } - memcpy( value->display_name, r->buffer + r->offset, keep ); - value->display_name[keep] = 0; - value->display_name_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->display_name[0] = 0; value->display_name_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 32 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 32 ); r->report->clamped++; } + memcpy( value->display_name, sub.buffer, (size_t) keep ); value->display_name[keep] = 0; value->display_name_length = (int32_t) keep; break; } - case 0x8617: /* health */ + case 0x7f69d4b5288ba9cfull: /* health */ { if ( kind != 10 ) { @@ -920,11 +1962,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->health = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->health = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xe7a6: /* mass */ + case 0x1f3757a2ce7b0ab1ull: /* mass */ { if ( kind != 10 ) { @@ -932,11 +1982,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->mass = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->mass = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xc4b2: /* hardpoints */ + case 0x95d0cce09ca82b73ull: /* hardpoints */ { if ( kind != 14 ) { @@ -944,45 +2002,68 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 4 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 4 ) { - int32_t decoded_v = (int32_t) table_reader_get32( &sub ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 8 ) { decoded_v = 8; r->report->clamped++; } - value->hardpoints[i] = decoded_v; + if ( !(table_kind_widens(elem_kind,4)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; } - decoded = i + 1; + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + int32_t previous_count = value->hardpoints_count; + value->hardpoints_count = 0; + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 2: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_hardpoints; } + int64_t decoded_wide = (int8_t) table_reader_get8( &sub ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 8ll ) { decoded_wide = 8ll; r->report->clamped++; } + value->hardpoints[i] = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; goto end_hardpoints; } + int64_t decoded_wide = (int16_t) table_reader_get16( &sub ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 8ll ) { decoded_wide = 8ll; r->report->clamped++; } + value->hardpoints[i] = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; goto end_hardpoints; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &sub ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 8 ) { decoded_v = 8; r->report->clamped++; } + value->hardpoints[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_hardpoints; + } + value->hardpoints_count = (int32_t) i + 1; + } + end_hardpoints: ; + { uint32_t tail; for (tail=(uint32_t)value->hardpoints_count;tail<(uint32_t)previous_count && tail<4;tail++) { + memset(&value->hardpoints[tail],0,sizeof(value->hardpoints[tail])); + } } } - value->hardpoints_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x2bc9: /* gunner */ + case 0x40dbb648c0cd44aaull: /* gunner */ { if ( kind != 13 ) { @@ -990,179 +2071,500 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ship_entry_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - gunner_settings_load_body( &sub, &value->gunner ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + gunner_settings_load_body( &sub, &value->gunner ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; gunner_settings_reset( &value->gunner ); } value->gunner_present = 1; break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int ship_entry_load( ShipEntry * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + ship_entry_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return ship_entry_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t global_settings_measure( const GlobalSettings * value ) +static SCHEMA_UNUSED int ship_entry_save_message_body(TableBitWriter * w,const ShipEntry * value) { - int64_t bytes = 2; /* terminator */ - if ( value->tick_rate != 60 ) { bytes += 3 + 4; } /* tick_rate */ - if ( value->difficulty != DIFFICULTY_NORMAL ) - { - uint16_t id_difficulty = 0; - switch ( value->difficulty ) /* Difficulty rides as the u16 hash of its VARIANT NAME (§5) */ - { - case DIFFICULTY_NONE: id_difficulty = 0; break; - case DIFFICULTY_EASY: id_difficulty = 0x7d08; break; - case DIFFICULTY_NORMAL: id_difficulty = 0x7fac; break; - case DIFFICULTY_HARD: id_difficulty = 0xc313; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) id_difficulty; - bytes += 3 + 2; /* difficulty: the variant's name hash */ - } - if ( value->build_note_length < 0 || value->build_note_length > 48 ) { return -1; } /* storage invariant */ - if ( value->build_note_length > 0 ) { bytes += 3 + 4 + value->build_note_length; } /* build_note */ + (void)value; + { /* display_name */ + if(value->display_name_length<0 || value->display_name_length>32) return 0; + if(value->display_name_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,95,kTableMessageRefBitsHere); + if(value->display_name_length<0 || value->display_name_length>32) return 0; + table_bit_put(w,(uint64_t)value->display_name_length,6); + table_bit_align_write(w); + table_bit_putbytes(w,value->display_name,value->display_name_length); + } + } + { /* health */ + if(!(value->health == 100.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->health),32); + } + } + { /* mass */ + if(!(value->mass == 1.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,15,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->mass),32); + } + } + { /* hardpoints */ + if(value->hardpoints_count<0 || value->hardpoints_count>4) return 0; + if(value->hardpoints_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,96,kTableMessageRefBitsHere); + if(value->hardpoints_count<0 || value->hardpoints_count>4) return 0; + table_bit_put(w,(uint64_t)value->hardpoints_count-0,3); + { int32_t i; + for(i=0;ihardpoints_count;i++) { + table_bit_put(w,(uint64_t)(value->hardpoints[i])-UINT64_C(0),4); + } } + } + } + { /* gunner */ + if(value->gunner_present) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,97,kTableMessageRefBitsHere); + if(!gunner_settings_save_message_body(w,&value->gunner)) return 0; + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t ship_entry_measure_messages(const ShipEntry * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x2d21d7cd66bd5a5d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,32); + memcpy(value->display_name,text,(size_t)kept); + value->display_name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->display_name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x7f69d4b5288ba9cf): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->health=decoded; + } + break; + } + case UINT64_C(0x1f3757a2ce7b0ab1): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->mass=decoded; + } + break; + } + case UINT64_C(0x95d0cce09ca82b73): { + if(!(entry->kind==14 && entry->elem_kind==4)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ihardpoints[i] : &scratch; + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>8ll){decoded=8ll; + r->report->clamped++; + } + (*item)=(int32_t)decoded; + } + }if(walkvalue_bits))goto malformed; + value->hardpoints_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x40dbb648c0cd44aa): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!gunner_settings_load_message_body(r,&value->gunner))return 0; + value->gunner_present=1; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int ship_entry_load_messages(ShipEntry * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_tabledemo_global_settings_wire_build_note_( TableWriter * w, const GlobalSettings * value ) +{ + if ( value->build_note_length < 0 || value->build_note_length > 48 ) { return 0; } + table_writer_raw( w, value->build_note, value->build_note_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_global_settings_wire_spawn_delays_( TableWriter * w, const GlobalSettings * value ) +{ + int32_t i; + table_writer_put8( w, 10 ); table_writer_leb( w, (uint64_t) 3 ); + for ( i = 0; i < 3; i++ ) { - int all_default_spawn_delays = 1; - int32_t i; - for ( i = 0; i < 3; i++ ) { if ( value->spawn_delays[i] != 0.0f ) { all_default_spawn_delays = 0; break; } } - if ( !all_default_spawn_delays ) - { - bytes += 3 + 4 + 5 + 12; /* spawn_delays */ - } + table_writer_put32( w, table_float_to_bits( value->spawn_delays[i] ) ); } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int global_settings_save_body( TableWriter * w, const GlobalSettings * value ) +static SCHEMA_UNUSED int global_settings_save_body( TableWriter * w, const GlobalSettings * value ) { - if ( value->tick_rate != 60 ) - { - table_writer_put16( w, 0x1953 ); table_writer_put8( w, 8 ); /* tick_rate */ - table_writer_put32( w, (uint32_t) ( value->tick_rate ) ); + (void) value; + { /* tick_rate */ + if ( !( value->tick_rate == 60 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa65f859b617deaf3ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->tick_rate ) ); + } } - if ( value->difficulty != DIFFICULTY_NORMAL ) - { - uint16_t id_difficulty = 0; - switch ( value->difficulty ) /* Difficulty rides as the u16 hash of its VARIANT NAME (§5) */ + { /* difficulty */ + if ( !( value->difficulty == DIFFICULTY_NORMAL ) ) { - case DIFFICULTY_NONE: id_difficulty = 0; break; - case DIFFICULTY_EASY: id_difficulty = 0x7d08; break; - case DIFFICULTY_NORMAL: id_difficulty = 0x7fac; break; - case DIFFICULTY_HARD: id_difficulty = 0xc313; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x467f35a74c6e4a70ull ); table_writer_put8( w, 30 ); + switch ( value->difficulty ) + { + case DIFFICULTY_NONE: table_writer_leb( w, 0 ); break; + case DIFFICULTY_EASY: table_writer_id( w, 0x483d9e6c1ccd1f9bull ); break; + case DIFFICULTY_NORMAL: table_writer_id( w, 0x9ff3121d30f88d52ull ); break; + case DIFFICULTY_HARD: table_writer_id( w, 0x58c7b5d87587287cull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xd53f ); table_writer_put8( w, 7 ); /* difficulty */ - table_writer_put16( w, id_difficulty ); } - if ( value->build_note_length < 0 || value->build_note_length > 48 ) { return 0; } /* storage invariant */ - if ( value->build_note_length > 0 ) - { - table_writer_put16( w, 0xb2d4 ); table_writer_put8( w, 12 ); /* build_note */ - table_writer_put32( w, (uint32_t) value->build_note_length ); - table_writer_raw( w, value->build_note, value->build_note_length ); + { /* build_note */ + if ( value->build_note_length < 0 || value->build_note_length > 48 ) { return 0; } + if ( value->build_note_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x14ec921cc2549d60ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_global_settings_wire_build_note_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_global_settings_wire_build_note_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_global_settings_wire_build_note_( w, value ) ) { return 0; } + } + } } - { - int all_default_spawn_delays = 1; - int32_t i; - for ( i = 0; i < 3; i++ ) { if ( value->spawn_delays[i] != 0.0f ) { all_default_spawn_delays = 0; break; } } - if ( !all_default_spawn_delays ) + { /* spawn_delays */ + int rides = 0; int32_t i; + for ( i = 0; i < 3; i++ ) { if ( !( value->spawn_delays[i] == 0.0f ) ) { rides = 1; break; } } + if ( rides ) { - int64_t len_at_spawn_delays; - table_writer_put16( w, 0x7c01 ); table_writer_put8( w, 14 ); /* spawn_delays (fixed [3]) */ - len_at_spawn_delays = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 10 ); table_writer_put32( w, 3 ); - for ( i = 0; i < 3; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x09ae5613b0051271ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_global_settings_wire_spawn_delays_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - table_writer_put32( w, table_float_to_bits( value->spawn_delays[i] ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_global_settings_wire_spawn_delays_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_global_settings_wire_spawn_delays_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_spawn_delays, (uint32_t) ( w->offset - len_at_spawn_delays - 4 ) ); } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t global_settings_measure( const GlobalSettings * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !global_settings_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t global_settings_save( const GlobalSettings * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !global_settings_save_body( &w, value ) ) { return -1; } - return w.offset; /* == global_settings_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int global_settings_load_body( TableReader * r, GlobalSettings * value ) +static SCHEMA_UNUSED int global_settings_load_body( TableReader * r, GlobalSettings * value ) { - global_settings_reset( value ); /* prefill declared defaults in place, then overlay */ + global_settings_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x1953: /* tick_rate */ + case 0xa65f859b617deaf3ull: /* tick_rate */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 240ull ) { decoded_wide = 240ull; r->report->clamped++; } + value->tick_rate = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 240ull ) { decoded_wide = 240ull; r->report->clamped++; } + value->tick_rate = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + else if ( decoded_v > 240 ) { decoded_v = 240; r->report->clamped++; } + value->tick_rate = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } - else if ( decoded_v > 240 ) { decoded_v = 240; r->report->clamped++; } - value->tick_rate = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 240ull ) { decoded_wide = 240ull; r->report->clamped++; } + value->tick_rate = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 240ull ) { decoded_wide = 240ull; r->report->clamped++; } + value->tick_rate = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + else if ( decoded_v > 240 ) { decoded_v = 240; r->report->clamped++; } + value->tick_rate = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xd53f: /* difficulty */ + case 0x467f35a74c6e4a70ull: /* difficulty */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->difficulty = DIFFICULTY_NONE; } + else { - case 0: value->difficulty = DIFFICULTY_NONE; break; - case 0x7d08: value->difficulty = DIFFICULTY_EASY; break; - case 0x7fac: value->difficulty = DIFFICULTY_NORMAL; break; - case 0xc313: value->difficulty = DIFFICULTY_HARD; break; - default: value->difficulty = DIFFICULTY_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0x483d9e6c1ccd1f9bull: value->difficulty = DIFFICULTY_EASY; break; + case 0x9ff3121d30f88d52ull: value->difficulty = DIFFICULTY_NORMAL; break; + case 0x58c7b5d87587287cull: value->difficulty = DIFFICULTY_HARD; break; + default: value->difficulty = DIFFICULTY_NONE; r->report->unknown++; break; + } } } break; } - case 0xb2d4: /* build_note */ + case 0x14ec921cc2549d60ull: /* build_note */ { if ( kind != 12 ) { @@ -1170,20 +2572,18 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int global_settings_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 48 ) { keep = 48; r->report->clamped++; } - memcpy( value->build_note, r->buffer + r->offset, keep ); - value->build_note[keep] = 0; - value->build_note_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->build_note[0] = 0; value->build_note_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 48 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 48 ); r->report->clamped++; } + memcpy( value->build_note, sub.buffer, (size_t) keep ); value->build_note[keep] = 0; value->build_note_length = (int32_t) keep; break; } - case 0x7c01: /* spawn_delays */ + case 0x09ae5613b0051271ull: /* spawn_delays */ { if ( kind != 14 ) { @@ -1191,306 +2591,610 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int global_settings_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - if ( elem_kind != 10 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 3 ) { keep = 3; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - value->spawn_delays[i] = table_bits_to_float( table_reader_get32( &sub ) ); + if ( elem_kind != 10 ) + { + if ( !(table_kind_widens(elem_kind,10)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 3 ) { kept = 3; r->report->clamped++; } + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 10: + { + if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; goto end_spawn_delays; } + value->spawn_delays[i] = table_bits_to_float( table_reader_get32( &sub ) ); + break; + } + default: r->report->malformed = 1; goto end_spawn_delays; + } + } + end_spawn_delays: ; } } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int global_settings_load( GlobalSettings * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + global_settings_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return global_settings_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t pack_config_measure( const PackConfig * value ) +static SCHEMA_UNUSED int global_settings_save_message_body(TableBitWriter * w,const GlobalSettings * value) +{ + (void)value; + { /* tick_rate */ + if(!(value->tick_rate == 60)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,7,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->tick_rate)-UINT64_C(1),8); + } + } + { /* difficulty */ + if(!(value->difficulty == DIFFICULTY_NORMAL)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,8,kTableMessageRefBitsHere); + switch(value->difficulty) { + case DIFFICULTY_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case DIFFICULTY_EASY: table_bit_put(w,110,kTableMessageRefBitsHere); + break; + case DIFFICULTY_NORMAL: table_bit_put(w,111,kTableMessageRefBitsHere); + break; + case DIFFICULTY_HARD: table_bit_put(w,112,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* build_note */ + if(value->build_note_length<0 || value->build_note_length>48) return 0; + if(value->build_note_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,9,kTableMessageRefBitsHere); + if(value->build_note_length<0 || value->build_note_length>48) return 0; + table_bit_put(w,(uint64_t)value->build_note_length,6); + table_bit_align_write(w); + table_bit_putbytes(w,value->build_note,value->build_note_length); + } + } + { /* spawn_delays */ + int rides=0; + { int32_t i; + for(i=0;i<3;i++) if(!(value->spawn_delays[i] == 0.0f)) { rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,10,kTableMessageRefBitsHere); + if(3<0 || 3>3) return 0; + table_bit_put(w,(uint64_t)3-3,0); + { int32_t i; + for(i=0;i<3;i++) { + table_bit_put(w,table_float_to_bits(value->spawn_delays[i]),32); + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t global_settings_measure_messages(const GlobalSettings * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xa65f859b617deaf3): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>240ull){decoded=240ull; + r->report->clamped++; + } + value->tick_rate=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x467f35a74c6e4a70): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->difficulty=DIFFICULTY_NONE; + else switch(named->id) { + case UINT64_C(0x483d9e6c1ccd1f9b): value->difficulty=DIFFICULTY_EASY; + break; + case UINT64_C(0x9ff3121d30f88d52): value->difficulty=DIFFICULTY_NORMAL; + break; + case UINT64_C(0x58c7b5d87587287c): value->difficulty=DIFFICULTY_HARD; + break; + default:value->difficulty=DIFFICULTY_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0x14ec921cc2549d60): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,48); + memcpy(value->build_note,text,(size_t)kept); + value->build_note[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->build_note_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x09ae5613b0051271): { + if(!(entry->kind==14 && entry->elem_kind==10)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>3?3:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ispawn_delays[i] : &scratch; + { float decoded; + if(element_entry->kind==10) {float narrow; + if(!table_message_float(r,element_entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + (*item)=decoded; + } + }if(walkvalue_bits))goto malformed; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int global_settings_load_messages(GlobalSettings * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +#define SCHEMA_TABLEDEMO_PACK_CONFIG_SHIPS_AT(value,key) SCHEMA_TABLE_KEYED_AT((value).ships,(key),SHIP_TYPE_MAX) +#define SCHEMA_TABLEDEMO_PACK_CONFIG_THRESHOLDS_AT(value,key) SCHEMA_TABLE_KEYED_AT((value).thresholds,(key),DIFFICULTY_MAX) +static SCHEMA_UNUSED int schema_tabledemo_pack_config_wire_ships_( TableWriter * w, const PackConfig * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->version != 1 ) { bytes += 3 + 4; } /* version */ + uint64_t count = 0; int32_t i; + for ( i = 0; i < SHIP_TYPE_MAX; i++ ) { - int64_t body_global = global_settings_measure( &value->global ); - if ( body_global < 0 ) { return -1; } - if ( body_global > 2 ) { bytes += 3 + 4 + body_global; } /* global: all-default nested elides */ + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !ship_entry_save_body( &slot_probe, &value->ships[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + count++; } + table_writer_put8( w, 13 ); table_writer_leb( w, count ); + for ( i = 0; i < SHIP_TYPE_MAX; i++ ) { - int64_t pairs_ships = 0, body_ships = 0; - int32_t i; - for ( i = 0; i < SHIP_TYPE_MAX; i++ ) /* [ShipType]: every stored slot is a named variant's */ + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !ship_entry_save_body( &slot_probe, &value->ships[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + switch ( i + 1 ) { - int64_t elem_bytes = ship_entry_measure( &value->ships[i] ); - if ( elem_bytes < 0 ) { return -1; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (ShipType) ( i + 1 ) ) /* ShipType rides as the u16 hash of its VARIANT NAME (§5) */ - { - case SHIP_TYPE_NONE: key_id = 0; break; - case SHIP_TYPE_FIGHTER: key_id = 0x412b; break; - case SHIP_TYPE_BOMBER: key_id = 0xb7ce; break; - case SHIP_TYPE_SCOUT: key_id = 0xd97a; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_ships++; body_ships += 2 + 4 + elem_bytes; /* key, length, body */ + case SHIP_TYPE_NONE: table_writer_leb( w, 0 ); break; + case SHIP_TYPE_FIGHTER: table_writer_id( w, 0xd011f7c3c15285c2ull ); break; + case SHIP_TYPE_BOMBER: table_writer_id( w, 0xa8216aa1c554cb8aull ); break; + case SHIP_TYPE_SCOUT: table_writer_id( w, 0x7d573c625719c1a5ull ); break; + default: return 0; /* no declared identity */ } - if ( pairs_ships > 0 ) { bytes += 3 + 4 + 5 + body_ships; } /* ships */ - } - { - int64_t pairs_thresholds = 0, body_thresholds = 0; - int32_t i; - for ( i = 0; i < DIFFICULTY_MAX; i++ ) /* [Difficulty]: every stored slot is a named variant's */ + if ( w->buffer == NULL ) { - if ( value->thresholds[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Difficulty) ( i + 1 ) ) /* Difficulty rides as the u16 hash of its VARIANT NAME (§5) */ - { - case DIFFICULTY_NONE: key_id = 0; break; - case DIFFICULTY_EASY: key_id = 0x7d08; break; - case DIFFICULTY_NORMAL: key_id = 0x7fac; break; - case DIFFICULTY_HARD: key_id = 0xc313; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_thresholds++; body_thresholds += 2 + 4 + 4; /* key, length, element */ + int64_t frame_begin = w->offset; + if ( !ship_entry_save_body( w, &value->ships[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - if ( pairs_thresholds > 0 ) { bytes += 3 + 4 + 5 + body_thresholds; } /* thresholds */ - } - if ( value->reserves_count < 0 || value->reserves_count > 3 ) { return -1; } /* storage invariant */ - if ( value->reserves_count > 0 ) - { - int32_t i; - bytes += 3 + 4 + 5; /* reserves */ - for ( i = 0; i < value->reserves_count; i++ ) + else { - int64_t elem_reserves = ship_entry_measure( &value->reserves[i] ); - if ( elem_reserves < 0 ) { return -1; } - bytes += 4 + elem_reserves; + TableWriter probe = table_writer_probe( w ); + if ( !ship_entry_save_body( &probe, &value->ships[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !ship_entry_save_body( w, &value->ships[i] ) ) { return 0; } } } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int pack_config_save_body( TableWriter * w, const PackConfig * value ) +static SCHEMA_UNUSED int schema_tabledemo_pack_config_wire_thresholds_( TableWriter * w, const PackConfig * value ) { - if ( value->version != 1 ) + uint64_t count = 0; int32_t i; + for ( i = 0; i < DIFFICULTY_MAX; i++ ) { - table_writer_put16( w, 0xe8e6 ); table_writer_put8( w, 8 ); /* version */ - table_writer_put32( w, (uint32_t) ( value->version ) ); + if ( value->thresholds[i] == 0 ) { continue; } + count++; } + table_writer_put8( w, 4 ); table_writer_leb( w, count ); + for ( i = 0; i < DIFFICULTY_MAX; i++ ) { - int64_t body_global = global_settings_measure( &value->global ); - if ( body_global < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_global > 2 ) /* all-default nested elides */ + if ( value->thresholds[i] == 0 ) { continue; } + switch ( i + 1 ) + { + case DIFFICULTY_NONE: table_writer_leb( w, 0 ); break; + case DIFFICULTY_EASY: table_writer_id( w, 0x483d9e6c1ccd1f9bull ); break; + case DIFFICULTY_NORMAL: table_writer_id( w, 0x9ff3121d30f88d52ull ); break; + case DIFFICULTY_HARD: table_writer_id( w, 0x58c7b5d87587287cull ); break; + default: return 0; /* no declared identity */ + } { - table_writer_put16( w, 0x1b51 ); table_writer_put8( w, 13 ); /* global */ - table_writer_put32( w, (uint32_t) body_global ); - if ( !global_settings_save_body( w, &value->global ) ) { return 0; } + TableWriter probe = table_writer_probe( w ); + TableWriter * outer = w; + w = &probe; + table_writer_put32( w, (uint32_t) ( value->thresholds[i] ) ); + w = outer; table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + table_writer_put32( w, (uint32_t) ( value->thresholds[i] ) ); } } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_pack_config_wire_reserves_( TableWriter * w, const PackConfig * value ) +{ + if ( value->reserves_count < 0 || value->reserves_count > 3 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->reserves_count ); + for ( i = 0; i < value->reserves_count; i++ ) { - uint32_t pairs_ships = 0; - int32_t i; - for ( i = 0; i < SHIP_TYPE_MAX; i++ ) /* [ShipType]: every stored slot is a named variant's */ + if ( w->buffer == NULL ) { - int64_t elem_bytes = ship_entry_measure( &value->ships[i] ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (ShipType) ( i + 1 ) ) /* ShipType rides as the u16 hash of its VARIANT NAME (§5) */ - { - case SHIP_TYPE_NONE: key_id = 0; break; - case SHIP_TYPE_FIGHTER: key_id = 0x412b; break; - case SHIP_TYPE_BOMBER: key_id = 0xb7ce; break; - case SHIP_TYPE_SCOUT: key_id = 0xd97a; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_ships++; + int64_t frame_begin = w->offset; + if ( !ship_entry_save_body( w, &value->reserves[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - if ( pairs_ships > 0 ) + else { - int64_t len_at_ships; - /* KIND 16, not 14: a keyed body and a positional one are - incompatible, so a reader of the other kind must see a kind - mismatch and skip, never misdecode (docs/SPEC-TABLES.md §3.2) */ - table_writer_put16( w, 0x2d39 ); table_writer_put8( w, 16 ); /* ships (keyed by ShipType) */ - len_at_ships = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, pairs_ships ); - /* ASCENDING BY VARIANT ORDINAL, which is slot order — this - writer's choice, and a reader must not rely on it: every - slot is found by its key (docs/SPEC-TABLES.md §3.2) */ - for ( i = 0; i < SHIP_TYPE_MAX; i++ ) + TableWriter probe = table_writer_probe( w ); + if ( !ship_entry_save_body( &probe, &value->reserves[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !ship_entry_save_body( w, &value->reserves[i] ) ) { return 0; } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int pack_config_save_body( TableWriter * w, const PackConfig * value ) +{ + (void) value; + { /* version */ + if ( !( value->version == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbb62c62c9808ea37ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->version ) ); + } + } + { /* global */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !global_settings_save_body( &default_probe, &value->global ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7fb43557b54149ceull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at_ships; - int64_t elem_bytes = ship_entry_measure( &value->ships[i] ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (ShipType) ( i + 1 ) ) /* ShipType rides as the u16 hash of its VARIANT NAME (§5) */ - { - case SHIP_TYPE_NONE: key_id = 0; break; - case SHIP_TYPE_FIGHTER: key_id = 0x412b; break; - case SHIP_TYPE_BOMBER: key_id = 0xb7ce; break; - case SHIP_TYPE_SCOUT: key_id = 0xd97a; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - table_writer_put16( w, key_id ); /* the slot's VARIANT id, not its position */ - elem_len_at_ships = w->offset; table_writer_put32( w, 0 ); - if ( !ship_entry_save_body( w, &value->ships[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at_ships, (uint32_t) ( w->offset - elem_len_at_ships - 4 ) ); + int64_t frame_begin = w->offset; + if ( !global_settings_save_body( w, &value->global ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !global_settings_save_body( &probe, &value->global ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !global_settings_save_body( w, &value->global ) ) { return 0; } } - table_writer_patch32( w, len_at_ships, (uint32_t) ( w->offset - len_at_ships - 4 ) ); } } - { - uint32_t pairs_thresholds = 0; - int32_t i; - for ( i = 0; i < DIFFICULTY_MAX; i++ ) /* [Difficulty]: every stored slot is a named variant's */ + { /* ships */ + int rides = 0; int32_t i; + for ( i = 0; i < SHIP_TYPE_MAX; i++ ) + { + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !ship_entry_save_body( &slot_probe, &value->ships[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + rides = 1; break; + } + if ( rides ) { - if ( value->thresholds[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Difficulty) ( i + 1 ) ) /* Difficulty rides as the u16 hash of its VARIANT NAME (§5) */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x294a5c4913e1ad44ull ); table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_pack_config_wire_ships_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - case DIFFICULTY_NONE: key_id = 0; break; - case DIFFICULTY_EASY: key_id = 0x7d08; break; - case DIFFICULTY_NORMAL: key_id = 0x7fac; break; - case DIFFICULTY_HARD: key_id = 0xc313; break; - default: return 0; /* no variant names this value: no wire identity */ + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_pack_config_wire_ships_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_pack_config_wire_ships_( w, value ) ) { return 0; } } - (void) key_id; - pairs_thresholds++; } - if ( pairs_thresholds > 0 ) + } + { /* thresholds */ + int rides = 0; int32_t i; + for ( i = 0; i < DIFFICULTY_MAX; i++ ) { - int64_t len_at_thresholds; - /* KIND 16, not 14: a keyed body and a positional one are - incompatible, so a reader of the other kind must see a kind - mismatch and skip, never misdecode (docs/SPEC-TABLES.md §3.2) */ - table_writer_put16( w, 0xb2eb ); table_writer_put8( w, 16 ); /* thresholds (keyed by Difficulty) */ - len_at_thresholds = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 4 ); table_writer_put32( w, pairs_thresholds ); - /* ASCENDING BY VARIANT ORDINAL, which is slot order — this - writer's choice, and a reader must not rely on it: every - slot is found by its key (docs/SPEC-TABLES.md §3.2) */ - for ( i = 0; i < DIFFICULTY_MAX; i++ ) + if ( value->thresholds[i] == 0 ) { continue; } + rides = 1; break; + } + if ( rides ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x9cda940a344e1571ull ); table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at_thresholds; - if ( value->thresholds[i] == 0 ) { continue; } /* a default slot elides */ - uint16_t key_id = 0; - switch ( (Difficulty) ( i + 1 ) ) /* Difficulty rides as the u16 hash of its VARIANT NAME (§5) */ - { - case DIFFICULTY_NONE: key_id = 0; break; - case DIFFICULTY_EASY: key_id = 0x7d08; break; - case DIFFICULTY_NORMAL: key_id = 0x7fac; break; - case DIFFICULTY_HARD: key_id = 0xc313; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - table_writer_put16( w, key_id ); /* the slot's VARIANT id, not its position */ - elem_len_at_thresholds = w->offset; table_writer_put32( w, 0 ); - table_writer_put32( w, (uint32_t) ( value->thresholds[i] ) ); - table_writer_patch32( w, elem_len_at_thresholds, (uint32_t) ( w->offset - elem_len_at_thresholds - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_pack_config_wire_thresholds_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_pack_config_wire_thresholds_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_pack_config_wire_thresholds_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_thresholds, (uint32_t) ( w->offset - len_at_thresholds - 4 ) ); } } - if ( value->reserves_count < 0 || value->reserves_count > 3 ) { return 0; } /* storage invariant */ - if ( value->reserves_count > 0 ) - { - int64_t len_at_reserves; - int32_t i; - table_writer_put16( w, 0x049d ); table_writer_put8( w, 14 ); /* reserves */ - len_at_reserves = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->reserves_count ); - for ( i = 0; i < value->reserves_count; i++ ) + { /* reserves */ + if ( value->reserves_count < 0 || value->reserves_count > 3 ) { return 0; } + if ( value->reserves_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x77707fccd201c228ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !ship_entry_save_body( w, &value->reserves[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_pack_config_wire_reserves_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_pack_config_wire_reserves_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_pack_config_wire_reserves_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_reserves, (uint32_t) ( w->offset - len_at_reserves - 4 ) ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t pack_config_measure( const PackConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !pack_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t pack_config_save( const PackConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !pack_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == pack_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int pack_config_load_body( TableReader * r, PackConfig * value ) +static SCHEMA_UNUSED int pack_config_load_body( TableReader * r, PackConfig * value ) { - pack_config_reset( value ); /* prefill declared defaults in place, then overlay */ + pack_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xe8e6: /* version */ + case 0xbb62c62c9808ea37ull: /* version */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->version = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->version = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->version = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->version = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->version = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->version = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->version = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x1b51: /* global */ + case 0x7fb43557b54149ceull: /* global */ { if ( kind != 13 ) { @@ -1498,18 +3202,13 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int pack_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - global_settings_load_body( &sub, &value->global ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + global_settings_load_body( &sub, &value->global ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; global_settings_reset( &value->global ); } break; } - case 0x2d39: /* ships */ + case 0x294a5c4913e1ad44ull: /* ships */ { if ( kind != 16 ) { @@ -1517,68 +3216,33 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int pack_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - TableReader sub; - uint32_t i; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); + uint8_t elem_kind; uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) { if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } r->report->widened++; } for ( i = 0; i < count; i++ ) { - uint16_t key; - uint32_t elem_len; - ShipType slot; - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - key = table_reader_get16( &sub ); - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - if ( key == 0 ) - { - /* None is the NULL KEY: 0 is the reserved id no declared - name can fold to, so a body carrying one is DAMAGED, not - merely foreign. Framing damage stops this body, keeps what - it decoded, and the parent reads on past the length - (docs/SPEC-TABLES.md §3.2, §4). */ - r->report->malformed = 1; - break; - } - slot = SHIP_TYPE_NONE; - { - int32_t before = r->report->unknown; - switch ( key ) - { - case 0: slot = SHIP_TYPE_NONE; break; - case 0x412b: slot = SHIP_TYPE_FIGHTER; break; - case 0xb7ce: slot = SHIP_TYPE_BOMBER; break; - case 0xd97a: slot = SHIP_TYPE_SCOUT; break; - default: slot = SHIP_TYPE_NONE; r->report->unknown++; break; /* an id this build cannot name */ - } - if ( r->report->unknown != before ) - { - sub.offset += elem_len; /* a slot this reader cannot name */ - continue; - } - } + uint64_t key_ref, key; int32_t slot = -1; TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - ship_entry_load_body( &elem, &value->ships[(int32_t) slot - 1] ); + case 0xd011f7c3c15285c2ull: slot = 0; break; + case 0xa8216aa1c554cb8aull: slot = 1; break; + case 0x7d573c625719c1a5ull: slot = 2; break; + default: r->report->unknown++; break; } - sub.offset += elem_len; + if ( slot < 0 ) { continue; } + ship_entry_load_body( &elem, &value->ships[slot] ); + if(elem.offset!=elem.size) { r->report->malformed=1; ship_entry_reset(&value->ships[slot]); } } } - r->offset = body_end; /* unread pairs and slack skip via the length */ break; } - case 0xb2eb: /* thresholds */ + case 0x9cda940a344e1571ull: /* thresholds */ { if ( kind != 16 ) { @@ -1586,74 +3250,65 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int pack_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - TableReader sub; - uint32_t i; - if ( elem_kind != 4 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); + uint8_t elem_kind; uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 4 ) { if ( !(table_kind_widens(elem_kind,4)) ) { r->report->kind_mismatch++; break; } r->report->widened++; } for ( i = 0; i < count; i++ ) { - uint16_t key; - uint32_t elem_len; - Difficulty slot; - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - key = table_reader_get16( &sub ); - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - if ( key == 0 ) + uint64_t key_ref, key; int32_t slot = -1; TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) { - /* None is the NULL KEY: 0 is the reserved id no declared - name can fold to, so a body carrying one is DAMAGED, not - merely foreign. Framing damage stops this body, keeps what - it decoded, and the parent reads on past the length - (docs/SPEC-TABLES.md §3.2, §4). */ - r->report->malformed = 1; - break; + case 0x483d9e6c1ccd1f9bull: slot = 0; break; + case 0x9ff3121d30f88d52ull: slot = 1; break; + case 0x58c7b5d87587287cull: slot = 2; break; + default: r->report->unknown++; break; } - slot = DIFFICULTY_NONE; + if ( slot < 0 ) { continue; } + switch ( elem_kind ) { - int32_t before = r->report->unknown; - switch ( key ) + case 2: { - case 0: slot = DIFFICULTY_NONE; break; - case 0x7d08: slot = DIFFICULTY_EASY; break; - case 0x7fac: slot = DIFFICULTY_NORMAL; break; - case 0xc313: slot = DIFFICULTY_HARD; break; - default: slot = DIFFICULTY_NONE; r->report->unknown++; break; /* an id this build cannot name */ + if ( !table_reader_has( &elem, 1 ) ) { r->report->malformed = 1; goto next_thresholds; } + int64_t decoded_wide = (int8_t) table_reader_get8( &elem ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->thresholds[slot] = decoded_wide; + break; } - if ( r->report->unknown != before ) + case 3: { - sub.offset += elem_len; /* a slot this reader cannot name */ - continue; + if ( !table_reader_has( &elem, 2 ) ) { r->report->malformed = 1; goto next_thresholds; } + int64_t decoded_wide = (int16_t) table_reader_get16( &elem ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->thresholds[slot] = decoded_wide; + break; } - } - { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - if ( !table_reader_has( &elem, 4 ) ) { r->report->malformed = 1; sub.offset += elem_len; continue; } + case 4: { - int32_t decoded_v = (int32_t) table_reader_get32( &elem ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } - value->thresholds[(int32_t) slot - 1] = decoded_v; + if ( !table_reader_has( &elem, 4 ) ) { r->report->malformed = 1; goto next_thresholds; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &elem ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->thresholds[slot] = decoded_v; + } + break; } + default: r->report->malformed = 1; goto next_thresholds; } - sub.offset += elem_len; + next_thresholds: ; } } - r->offset = body_end; /* unread pairs and slack skip via the length */ break; } - case 0x049d: /* reserves */ + case 0x77707fccd201c228ull: /* reserves */ { if ( kind != 14 ) { @@ -1661,65 +3316,444 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int pack_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 3 ) { keep = 3; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 3 ) { kept = 3; r->report->clamped++; } + int32_t previous_count = value->reserves_count; + value->reserves_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_reserves; } ship_entry_load_body( &elem, &value->reserves[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; ship_entry_reset(&value->reserves[i]); } + value->reserves_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_reserves: ; + { uint32_t tail; for (tail=(uint32_t)value->reserves_count;tail<(uint32_t)previous_count && tail<3;tail++) { + ship_entry_reset(&value->reserves[tail]); + } } } - value->reserves_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int pack_config_load( PackConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + pack_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return pack_config_load_body( &r, value ); } +static SCHEMA_UNUSED int pack_config_save_message_body(TableBitWriter * w,const PackConfig * value) +{ + (void)value; + { /* version */ + if(!(value->version == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,27,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->version)-UINT64_C(0),32); + } + } + { /* global */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!global_settings_save_message_body(&probe,&value->global)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,28,kTableMessageRefBitsHere); + if(!global_settings_save_message_body(w,&value->global)) return 0; + } + } + { /* ships */ + int rides=0; + { int32_t i; + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!ship_entry_save_message_body(&probe,&value->ships[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,29,kTableMessageRefBitsHere); + { uint32_t count=0; + int32_t i; + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!ship_entry_save_message_body(&probe,&value->ships[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + count++; + } + table_bit_put(w,count,2); + for(i=0;i<3;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!ship_entry_save_message_body(&probe,&value->ships[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + switch(i+1) { + case SHIP_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case SHIP_TYPE_FIGHTER: table_bit_put(w,119,kTableMessageRefBitsHere); + break; + case SHIP_TYPE_BOMBER: table_bit_put(w,120,kTableMessageRefBitsHere); + break; + case SHIP_TYPE_SCOUT: table_bit_put(w,121,kTableMessageRefBitsHere); + break; + default: return 0; + } + if(!ship_entry_save_message_body(w,&value->ships[i])) return 0; + } } + } + } + { /* thresholds */ + int rides=0; + { int32_t i; + for(i=0;i<3;i++) { + if(value->thresholds[i] == 0) continue; + rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,30,kTableMessageRefBitsHere); + { uint32_t count=0; + int32_t i; + for(i=0;i<3;i++) { + if(value->thresholds[i] == 0) continue; + count++; + } + table_bit_put(w,count,2); + for(i=0;i<3;i++) { + if(value->thresholds[i] == 0) continue; + switch(i+1) { + case DIFFICULTY_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case DIFFICULTY_EASY: table_bit_put(w,110,kTableMessageRefBitsHere); + break; + case DIFFICULTY_NORMAL: table_bit_put(w,111,kTableMessageRefBitsHere); + break; + case DIFFICULTY_HARD: table_bit_put(w,112,kTableMessageRefBitsHere); + break; + default: return 0; + } + table_bit_put(w,(uint64_t)(value->thresholds[i])-UINT64_C(0),10); + } } + } + } + { /* reserves */ + if(value->reserves_count<0 || value->reserves_count>3) return 0; + if(value->reserves_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,31,kTableMessageRefBitsHere); + if(value->reserves_count<0 || value->reserves_count>3) return 0; + table_bit_put(w,(uint64_t)value->reserves_count-0,2); + { int32_t i; + for(i=0;ireserves_count;i++) { + if(!ship_entry_save_message_body(w,&value->reserves[i])) return 0; + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t pack_config_measure_messages(const PackConfig * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xbb62c62c9808ea37): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->version=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x7fb43557b54149ce): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!global_settings_load_message_body(r,&value->global))return 0; + break; + } + case UINT64_C(0x294a5c4913e1ad44): { + if(!(entry->kind==16 && entry->elem_kind==13)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0xd011f7c3c15285c2):slot=0; + break; + case UINT64_C(0xa8216aa1c554cb8a):slot=1; + break; + case UINT64_C(0x7d573c625719c1a5):slot=2; + break; + default:break; + } if(slot<0){r->report->unknown++; + { ShipEntry scratch; + memset(&scratch,0,sizeof(scratch)); + if(!ship_entry_load_message_body(r,&scratch))return 0; + }continue; + } + if(!ship_entry_load_message_body(r,&value->ships[slot]))return 0; + } } + break; + } + case UINT64_C(0x9cda940a344e1571): { + if(!(entry->kind==16 && entry->elem_kind==4)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0x483d9e6c1ccd1f9b):slot=0; + break; + case UINT64_C(0x9ff3121d30f88d52):slot=1; + break; + case UINT64_C(0x58c7b5d87587287c):slot=2; + break; + default:break; + } if(slot<0){r->report->unknown++; + { int32_t scratch; + memset(&scratch,0,sizeof(scratch)); + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>1000ll){decoded=1000ll; + r->report->clamped++; + } + scratch=(int32_t)decoded; + } + }continue; + } + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>1000ll){decoded=1000ll; + r->report->clamped++; + } + value->thresholds[slot]=(int32_t)decoded; + } + } } + break; + } + case UINT64_C(0x77707fccd201c228): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>3?3:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ireserves[i] : &scratch; + if(!ship_entry_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->reserves_count=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int pack_config_load_messages(PackConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define gunner_settings_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define gunner_settings_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define gunner_settings_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define gunner_settings_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define gunner_settings_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ship_entry_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ship_entry_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ship_entry_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ship_entry_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define ship_entry_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define global_settings_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define global_settings_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define global_settings_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define global_settings_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define global_settings_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define pack_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define pack_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define pack_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define pack_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define pack_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* gunner_settings_open: match the header and POINT. On a match the bytes ARE what this @@ -1741,6 +3775,8 @@ static SCHEMA_UNUSED int pack_config_load( PackConfig * value, const uint8_t * b file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const GunnerSettings * gunner_settings_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const GunnerSettings *)table_cook_open_ex(bytes,length,sizeof(GunnerSettings),SCHEMA_TABLE_ALIGNOF(GunnerSettings),reason); } static SCHEMA_UNUSED const GunnerSettings * gunner_settings_open( const void * bytes, uint64_t length ) { return (const GunnerSettings *) table_cook_open( bytes, length, (uint64_t) sizeof( GunnerSettings ), (uint64_t) SCHEMA_TABLE_ALIGNOF( GunnerSettings ) ); @@ -1765,6 +3801,8 @@ static SCHEMA_UNUSED const GunnerSettings * gunner_settings_open( const void * b file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const ShipEntry * ship_entry_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const ShipEntry *)table_cook_open_ex(bytes,length,sizeof(ShipEntry),SCHEMA_TABLE_ALIGNOF(ShipEntry),reason); } static SCHEMA_UNUSED const ShipEntry * ship_entry_open( const void * bytes, uint64_t length ) { return (const ShipEntry *) table_cook_open( bytes, length, (uint64_t) sizeof( ShipEntry ), (uint64_t) SCHEMA_TABLE_ALIGNOF( ShipEntry ) ); @@ -1789,6 +3827,8 @@ static SCHEMA_UNUSED const ShipEntry * ship_entry_open( const void * bytes, uint file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const GlobalSettings * global_settings_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const GlobalSettings *)table_cook_open_ex(bytes,length,sizeof(GlobalSettings),SCHEMA_TABLE_ALIGNOF(GlobalSettings),reason); } static SCHEMA_UNUSED const GlobalSettings * global_settings_open( const void * bytes, uint64_t length ) { return (const GlobalSettings *) table_cook_open( bytes, length, (uint64_t) sizeof( GlobalSettings ), (uint64_t) SCHEMA_TABLE_ALIGNOF( GlobalSettings ) ); @@ -1813,11 +3853,110 @@ static SCHEMA_UNUSED const GlobalSettings * global_settings_open( const void * b file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const PackConfig * pack_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const PackConfig *)table_cook_open_ex(bytes,length,sizeof(PackConfig),SCHEMA_TABLE_ALIGNOF(PackConfig),reason); } static SCHEMA_UNUSED const PackConfig * pack_config_open( const void * bytes, uint64_t length ) { return (const PackConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( PackConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( PackConfig ) ); } +static SCHEMA_UNUSED int schema_tabledemo_gunner_settings_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const GunnerSettings * value=(const GunnerSettings *)storage; + (void)n; (void)at; (void)value; (void)order; + { uint32_t bits; memcpy(&bits,&value->reaction,4); table_cook_put(at+0,bits,4,order); } + table_cook_put(at+4,value->tracking ? 1 : 0,1,order); + table_cook_bytes(at+5,value->callsign,value->callsign_length,25); + table_cook_put(at+5+27,(uint32_t)value->callsign_length,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t gunner_settings_cook_measure(const GunnerSettings * value) { (void)value; return 120; } +static SCHEMA_UNUSED int gunner_settings_cook(const GunnerSettings * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<120) { return 0; } + memset(raw,0,120); + if(!schema_tabledemo_gunner_settings_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+112,UINT64_C(0xb76842b2253337af),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,40,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_ship_entry_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const ShipEntry * value=(const ShipEntry *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->display_name,value->display_name_length,33); + table_cook_put(at+0+36,(uint32_t)value->display_name_length,4,order); + { uint32_t bits; memcpy(&bits,&value->health,4); table_cook_put(at+40,bits,4,order); } + { uint32_t bits; memcpy(&bits,&value->mass,4); table_cook_put(at+44,bits,4,order); } + { int32_t cook_i1; for(cook_i1=0;cook_i1<4;cook_i1++) { + table_cook_put(at+48+cook_i1*4,(uint64_t)value->hardpoints[cook_i1],4,order); + } } + table_cook_put(at+48+16,(uint32_t)value->hardpoints_count,4,order); + table_cook_put(at+68+36,value->gunner_present ? 1 : 0,1,order); + if(!schema_tabledemo_gunner_settings_cook_body_(n,at+68,&value->gunner,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t ship_entry_cook_measure(const ShipEntry * value) { (void)value; return 192; } +static SCHEMA_UNUSED int ship_entry_cook(const ShipEntry * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<192) { return 0; } + memset(raw,0,192); + if(!schema_tabledemo_ship_entry_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+184,UINT64_C(0x1d1a9911da171a29),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,112,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_global_settings_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const GlobalSettings * value=(const GlobalSettings *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->tick_rate,4,order); + table_cook_put(at+4,(uint64_t)value->difficulty,1,order); + table_cook_bytes(at+5,value->build_note,value->build_note_length,49); + table_cook_put(at+5+51,(uint32_t)value->build_note_length,4,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<3;cook_i1++) { + { uint32_t bits; memcpy(&bits,&value->spawn_delays[cook_i1],4); table_cook_put(at+60+cook_i1*4,bits,4,order); } + } } + return 1; +} +static SCHEMA_UNUSED int64_t global_settings_cook_measure(const GlobalSettings * value) { (void)value; return 152; } +static SCHEMA_UNUSED int global_settings_cook(const GlobalSettings * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<152) { return 0; } + memset(raw,0,152); + if(!schema_tabledemo_global_settings_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+144,UINT64_C(0xffb1dfb1f6ce215b),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,72,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_pack_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const PackConfig * value=(const PackConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->version,4,order); + if(!schema_tabledemo_global_settings_cook_body_(n,at+4,&value->global,order)) { return 0; } + { int32_t cook_i1; for(cook_i1=0;cook_i1<3;cook_i1++) { + if(!schema_tabledemo_ship_entry_cook_body_(n,at+76+cook_i1*108,&value->ships[cook_i1],order)) { return 0; } + } } + { int32_t cook_i2; for(cook_i2=0;cook_i2<3;cook_i2++) { + table_cook_put(at+400+cook_i2*4,(uint64_t)value->thresholds[cook_i2],4,order); + } } + { int32_t cook_i3; for(cook_i3=0;cook_i3<3;cook_i3++) { + if(!schema_tabledemo_ship_entry_cook_body_(n,at+412+cook_i3*108,&value->reserves[cook_i3],order)) { return 0; } + } } + table_cook_put(at+412+324,(uint32_t)value->reserves_count,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t pack_config_cook_measure(const PackConfig * value) { (void)value; return 824; } +static SCHEMA_UNUSED int pack_config_cook(const PackConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<824) { return 0; } + memset(raw,0,824); + if(!schema_tabledemo_pack_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+816,UINT64_C(0xd02d825db114336e),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,744,1,8,order); return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/examples-c/RangesTable.c b/testdata/golden/tables/examples-c/RangesTable.c index bfe457f9c..553cad78a 100644 --- a/testdata/golden/tables/examples-c/RangesTable.c +++ b/testdata/golden/tables/examples-c/RangesTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) - { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,56 +2330,56 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableFieldInfo schema_tabledemo_ranged_signed_fields_[] = { - { "i8_span", "i8_span", "int8", 0x8d4f, 2, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i8_span ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i8_span ), 0xffffffffu, 0xffffffffu, NULL, 1, -128.0, 127.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i8_low", "i8_low", "int8", 0xe337, 2, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i8_low ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i8_low ), 0xffffffffu, 0xffffffffu, NULL, 1, -128.0, 126.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i8_high", "i8_high", "int8", 0x6a5f, 2, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i8_high ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i8_high ), 0xffffffffu, 0xffffffffu, NULL, 1, -127.0, 127.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i8_inside", "i8_inside", "int8", 0x8eee, 2, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i8_inside ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i8_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, -127.0, 126.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i16_span", "i16_span", "int16", 0x0a1e, 3, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i16_span ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i16_span ), 0xffffffffu, 0xffffffffu, NULL, 1, -32768.0, 32767.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i16_low", "i16_low", "int16", 0xaaae, 3, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i16_low ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i16_low ), 0xffffffffu, 0xffffffffu, NULL, 1, -32768.0, 32766.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i16_high", "i16_high", "int16", 0xe8f2, 3, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i16_high ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i16_high ), 0xffffffffu, 0xffffffffu, NULL, 1, -32767.0, 32767.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i16_inside", "i16_inside", "int16", 0x1a0e, 3, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i16_inside ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i16_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, -32767.0, 32766.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i32_span", "i32_span", "int32", 0x6bd1, 4, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i32_span ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i32_span ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.147483648e+09, 2.147483647e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i32_low", "i32_low", "int32", 0xb8e0, 4, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i32_low ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i32_low ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.147483648e+09, 2.147483646e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i32_high", "i32_high", "int32", 0x3806, 4, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i32_high ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i32_high ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.147483647e+09, 2.147483647e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i32_inside", "i32_inside", "int32", 0x9a24, 4, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i32_inside ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i32_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.147483647e+09, 2.147483646e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i64_span", "i64_span", "int64", 0xf086, 5, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i64_span ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i64_span ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 9.223372036854776e+18, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i64_low", "i64_low", "int64", 0xc705, 5, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i64_low ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i64_low ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 9.223372036854776e+18, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i64_high", "i64_high", "int64", 0x328e, 5, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i64_high ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i64_high ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 9.223372036854776e+18, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "i64_inside", "i64_inside", "int64", 0x7d51, 5, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i64_inside ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i64_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 9.223372036854776e+18, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "edges", "edges", "int16", 0xb1cb, 3, 1, 1, 0, 4, (uint32_t) offsetof( RangedSigned, edges ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->edges[0] ), (uint32_t) offsetof( RangedSigned, edges_count ), 0xffffffffu, NULL, 1, -32768.0, 32767.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "i8_span", "i8_span", "int8", 0x48121511bf702eb5ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i8_span ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i8_span ), 0xffffffffu, 0xffffffffu, NULL, 1, -128.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i8_low", "i8_low", "int8", 0x942bfe3168090f7dull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i8_low ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i8_low ), 0xffffffffu, 0xffffffffu, NULL, 1, -128.0, 126.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i8_high", "i8_high", "int8", 0xd182acd105b55dd1ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i8_high ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i8_high ), 0xffffffffu, 0xffffffffu, NULL, 1, -127.0, 127.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i8_inside", "i8_inside", "int8", 0xef9ded23c8912a81ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i8_inside ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i8_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, -127.0, 126.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i16_span", "i16_span", "int16", 0xb655574ea23760b4ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i16_span ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i16_span ), 0xffffffffu, 0xffffffffu, NULL, 1, -32768.0, 32767.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i16_low", "i16_low", "int16", 0xd217b3af8d7a2bdeull, 3, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i16_low ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i16_low ), 0xffffffffu, 0xffffffffu, NULL, 1, -32768.0, 32766.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i16_high", "i16_high", "int16", 0x90652f70c9127900ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i16_high ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i16_high ), 0xffffffffu, 0xffffffffu, NULL, 1, -32767.0, 32767.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i16_inside", "i16_inside", "int16", 0x6a659d7c354db158ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i16_inside ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i16_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, -32767.0, 32766.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i32_span", "i32_span", "int32", 0xe693bd88532c91d6ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i32_span ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i32_span ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.147483648e+09, 2.147483647e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i32_low", "i32_low", "int32", 0x065ee827cf99fbb4ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i32_low ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i32_low ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.147483648e+09, 2.147483646e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i32_high", "i32_high", "int32", 0xc9b121c4c2a186eeull, 4, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i32_high ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i32_high ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.147483647e+09, 2.147483647e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i32_inside", "i32_inside", "int32", 0xd363dfa465acf27aull, 4, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i32_inside ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i32_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, -2.147483647e+09, 2.147483646e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i64_span", "i64_span", "int64", 0x7549c70700c49d6full, 5, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i64_span ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i64_span ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 9.223372036854776e+18, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i64_low", "i64_low", "int64", 0x1cce6617419771dbull, 5, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i64_low ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i64_low ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 9.223372036854776e+18, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i64_high", "i64_high", "int64", 0x3b54fa60620b5597ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i64_high ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i64_high ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 9.223372036854776e+18, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "i64_inside", "i64_inside", "int64", 0xd308fcb98ece5d23ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( RangedSigned, i64_inside ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->i64_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, -9.223372036854776e+18, 9.223372036854776e+18, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "edges", "edges", "int16", 0xc70cde6b85b6197dull, 3, 1, 1, 0, 4, (uint32_t) offsetof( RangedSigned, edges ), (uint32_t) sizeof( ( (RangedSigned *) 0 )->edges[0] ), (uint32_t) offsetof( RangedSigned, edges_count ), 0xffffffffu, NULL, 1, -32768.0, 32767.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_ranged_signed_info_ = { "RangedSigned", (uint32_t) sizeof( RangedSigned ), 17, schema_tabledemo_ranged_signed_fields_, schema_tabledemo_ranged_signed_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_ranged_unsigned_fields_[] = { - { "u8_span", "u8_span", "uint8", 0x6434, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u8_span ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u8_span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u8_low", "u8_low", "uint8", 0xb24e, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u8_low ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u8_low ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 254.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u8_high", "u8_high", "uint8", 0x202a, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u8_high ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u8_high ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 255.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u8_inside", "u8_inside", "uint8", 0x8fbf, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u8_inside ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u8_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 254.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u16_span", "u16_span", "uint16", 0xd410, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u16_span ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u16_span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u16_low", "u16_low", "uint16", 0x6f72, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u16_low ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u16_low ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65534.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u16_high", "u16_high", "uint16", 0x1364, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u16_high ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u16_high ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 65535.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u16_inside", "u16_inside", "uint16", 0xbf32, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u16_inside ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u16_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 65534.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u32_span", "u32_span", "uint32", 0x0513, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u32_span ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u32_span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u32_low", "u32_low", "uint32", 0x4cce, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u32_low ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u32_low ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967294e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u32_high", "u32_high", "uint32", 0xa2da, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u32_high ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u32_high ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 4.294967295e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u32_inside", "u32_inside", "uint32", 0xf596, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u32_inside ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u32_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 4.294967294e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u64_span", "u64_span", "uint64", 0x6e8d, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u64_span ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u64_span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u64_low", "u64_low", "uint64", 0x95b5, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u64_low ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u64_low ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u64_high", "u64_high", "uint64", 0x15e4, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u64_high ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u64_high ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 1.8446744073709552e+19, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "u64_inside", "u64_inside", "uint64", 0x2ce0, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u64_inside ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u64_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 1.8446744073709552e+19, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "counts", "counts", "uint64", 0xe27a, 9, 1, 1, 0, 4, (uint32_t) offsetof( RangedUnsigned, counts ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->counts[0] ), (uint32_t) offsetof( RangedUnsigned, counts_count ), 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "u8_span", "u8_span", "uint8", 0x0f8897557f37c021ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u8_span ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u8_span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u8_low", "u8_low", "uint8", 0x2e17553f8200a2a1ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u8_low ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u8_low ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 254.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u8_high", "u8_high", "uint8", 0xa0e5c60df9301b7dull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u8_high ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u8_high ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u8_inside", "u8_inside", "uint8", 0x1cb2c073a6f5844dull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u8_inside ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u8_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 254.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u16_span", "u16_span", "uint16", 0x4ca0c150b1790960ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u16_span ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u16_span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u16_low", "u16_low", "uint16", 0xf252136836b04cb2ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u16_low ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u16_low ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65534.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u16_high", "u16_high", "uint16", 0x4f37e1f216e7610cull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u16_high ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u16_high ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u16_inside", "u16_inside", "uint16", 0xd176b605978f3304ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u16_inside ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u16_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 65534.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u32_span", "u32_span", "uint32", 0x200b924de7479cdaull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u32_span ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u32_span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u32_low", "u32_low", "uint32", 0xa53d2f2a31b5acb0ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u32_low ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u32_low ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967294e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u32_high", "u32_high", "uint32", 0x9759be8ea1a4e3d2ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u32_high ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u32_high ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u32_inside", "u32_inside", "uint32", 0x8dc515fc082e3c0eull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u32_inside ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u32_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 4.294967294e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u64_span", "u64_span", "uint64", 0xbeecef11dbdf8973ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u64_span ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u64_span ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u64_low", "u64_low", "uint64", 0x0117ad66e0fff227ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u64_low ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u64_low ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u64_high", "u64_high", "uint64", 0xf2b45af3b50689dbull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u64_high ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u64_high ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "u64_inside", "u64_inside", "uint64", 0x713e12017eebcaf7ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedUnsigned, u64_inside ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->u64_inside ), 0xffffffffu, 0xffffffffu, NULL, 1, 1.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "counts", "counts", "uint64", 0xc341febe5aae51e5ull, 9, 1, 1, 0, 4, (uint32_t) offsetof( RangedUnsigned, counts ), (uint32_t) sizeof( ( (RangedUnsigned *) 0 )->counts[0] ), (uint32_t) offsetof( RangedUnsigned, counts_count ), 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_ranged_unsigned_info_ = { "RangedUnsigned", (uint32_t) sizeof( RangedUnsigned ), 17, schema_tabledemo_ranged_unsigned_fields_, schema_tabledemo_ranged_unsigned_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_ranged_widths_fields_[] = { - { "b8", "b8", "bits(8)", 0xa7cb, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b8 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b8 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "b16", "b16", "bits(16)", 0xb643, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b16 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b16 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "b32", "b32", "bits(32)", 0x33f2, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b32 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b32 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "b64", "b64", "bits(64)", 0x75dc, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b64 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b64 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "b12", "b12", "bits(12)", 0xbcf7, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b12 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b12 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "b48", "b48", "bits(48)", 0x9797, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b48 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b48 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "b8", "b8", "bits(8)", 0x08a60c07b54d8dc7ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b8 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b8 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 255.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b16", "b16", "bits(16)", 0xff95701912ad97beull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b16 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b16 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 65535.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b32", "b32", "bits(32)", 0xff9c5c1912b39470ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b32 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b32 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.294967295e+09, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b64", "b64", "bits(64)", 0xff92701912ab61e7ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b64 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b64 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1.8446744073709552e+19, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b12", "b12", "bits(12)", 0xff95741912ad9e8aull, 7, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b12 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b12 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4095.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b48", "b48", "bits(48)", 0xff8b681912a535a1ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( RangedWidths, b48 ), (uint32_t) sizeof( ( (RangedWidths *) 0 )->b48 ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 2.81474976710655e+14, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_ranged_widths_info_ = { "RangedWidths", (uint32_t) sizeof( RangedWidths ), 6, schema_tabledemo_ranged_widths_fields_, schema_tabledemo_ranged_widths_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/examples-c/RangesTable.h b/testdata/golden/tables/examples-c/RangesTable.h index 26154cd4f..1269ce31d 100644 --- a/testdata/golden/tables/examples-c/RangesTable.h +++ b/testdata/golden/tables/examples-c/RangesTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package tabledemo — protocol id 0xba7b344f28584d33 (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_TABLEDEMO_RANGESTABLE_H #define SCHEMA_TABLEDEMO_RANGESTABLE_H @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -183,6 +194,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -210,6 +223,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -231,157 +246,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[156]; + uint32_t slots[512], positions[156]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (512-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(512-1); + } + if(v->count==156) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } } - return 0; + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -391,6 +526,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_TABLEDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_TABLEDEMO_BUILD_VERSION #define SCHEMA_TABLEDEMO_BUILD_VERSION @@ -502,25 +692,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -530,40 +720,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_TABLEDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table RangedSigned — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in ranged_signed_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -705,155 +1476,219 @@ static SCHEMA_UNUSED int64_t ranged_widths_measure( const RangedWidths * value ) static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_widths_save_body( TableWriter * w, const RangedWidths * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_widths_load_body( TableReader * r, RangedWidths * value ); -static SCHEMA_UNUSED int64_t ranged_signed_measure( const RangedSigned * value ) +static SCHEMA_UNUSED int schema_tabledemo_ranged_signed_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_ranged_unsigned_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_ranged_widths_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int ranged_signed_save_message_body(TableBitWriter *,const RangedSigned *); +static SCHEMA_UNUSED int ranged_unsigned_save_message_body(TableBitWriter *,const RangedUnsigned *); +static SCHEMA_UNUSED int ranged_widths_save_message_body(TableBitWriter *,const RangedWidths *); +static SCHEMA_UNUSED int ranged_signed_load_message_body(TableMessageReader *,RangedSigned *); +static SCHEMA_UNUSED int ranged_unsigned_load_message_body(TableMessageReader *,RangedUnsigned *); +static SCHEMA_UNUSED int ranged_widths_load_message_body(TableMessageReader *,RangedWidths *); +static SCHEMA_UNUSED int schema_tabledemo_ranged_signed_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_ranged_unsigned_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_ranged_widths_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_ranged_signed_wire_edges_( TableWriter * w, const RangedSigned * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->i8_span != 0 ) { bytes += 3 + 1; } /* i8_span */ - if ( value->i8_low != 0 ) { bytes += 3 + 1; } /* i8_low */ - if ( value->i8_high != 0 ) { bytes += 3 + 1; } /* i8_high */ - if ( value->i8_inside != 0 ) { bytes += 3 + 1; } /* i8_inside */ - if ( value->i16_span != 0 ) { bytes += 3 + 2; } /* i16_span */ - if ( value->i16_low != 0 ) { bytes += 3 + 2; } /* i16_low */ - if ( value->i16_high != 0 ) { bytes += 3 + 2; } /* i16_high */ - if ( value->i16_inside != 0 ) { bytes += 3 + 2; } /* i16_inside */ - if ( value->i32_span != 0 ) { bytes += 3 + 4; } /* i32_span */ - if ( value->i32_low != 0 ) { bytes += 3 + 4; } /* i32_low */ - if ( value->i32_high != 0 ) { bytes += 3 + 4; } /* i32_high */ - if ( value->i32_inside != 0 ) { bytes += 3 + 4; } /* i32_inside */ - if ( value->i64_span != 0 ) { bytes += 3 + 8; } /* i64_span */ - if ( value->i64_low != 0 ) { bytes += 3 + 8; } /* i64_low */ - if ( value->i64_high != 0 ) { bytes += 3 + 8; } /* i64_high */ - if ( value->i64_inside != 0 ) { bytes += 3 + 8; } /* i64_inside */ - if ( value->edges_count < 0 || value->edges_count > 4 ) { return -1; } /* storage invariant */ - if ( value->edges_count > 0 ) + if ( value->edges_count < 0 || value->edges_count > 4 ) { return 0; } + int32_t i; + table_writer_put8( w, 3 ); table_writer_leb( w, (uint64_t) value->edges_count ); + for ( i = 0; i < value->edges_count; i++ ) { - bytes += 3 + 4 + 5 + (int64_t) value->edges_count * 2; /* edges */ + table_writer_put16( w, (uint16_t) ( value->edges[i] ) ); } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_signed_save_body( TableWriter * w, const RangedSigned * value ) +static SCHEMA_UNUSED int ranged_signed_save_body( TableWriter * w, const RangedSigned * value ) { - if ( value->i8_span != 0 ) - { - table_writer_put16( w, 0x8d4f ); table_writer_put8( w, 2 ); /* i8_span */ - table_writer_put8( w, (uint8_t) ( value->i8_span ) ); + (void) value; + { /* i8_span */ + if ( !( value->i8_span == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x48121511bf702eb5ull ); table_writer_put8( w, 2 ); + table_writer_put8( w, (uint8_t) ( value->i8_span ) ); + } } - if ( value->i8_low != 0 ) - { - table_writer_put16( w, 0xe337 ); table_writer_put8( w, 2 ); /* i8_low */ - table_writer_put8( w, (uint8_t) ( value->i8_low ) ); + { /* i8_low */ + if ( !( value->i8_low == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x942bfe3168090f7dull ); table_writer_put8( w, 2 ); + table_writer_put8( w, (uint8_t) ( value->i8_low ) ); + } } - if ( value->i8_high != 0 ) - { - table_writer_put16( w, 0x6a5f ); table_writer_put8( w, 2 ); /* i8_high */ - table_writer_put8( w, (uint8_t) ( value->i8_high ) ); + { /* i8_high */ + if ( !( value->i8_high == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd182acd105b55dd1ull ); table_writer_put8( w, 2 ); + table_writer_put8( w, (uint8_t) ( value->i8_high ) ); + } } - if ( value->i8_inside != 0 ) - { - table_writer_put16( w, 0x8eee ); table_writer_put8( w, 2 ); /* i8_inside */ - table_writer_put8( w, (uint8_t) ( value->i8_inside ) ); + { /* i8_inside */ + if ( !( value->i8_inside == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xef9ded23c8912a81ull ); table_writer_put8( w, 2 ); + table_writer_put8( w, (uint8_t) ( value->i8_inside ) ); + } } - if ( value->i16_span != 0 ) - { - table_writer_put16( w, 0x0a1e ); table_writer_put8( w, 3 ); /* i16_span */ - table_writer_put16( w, (uint16_t) ( value->i16_span ) ); + { /* i16_span */ + if ( !( value->i16_span == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb655574ea23760b4ull ); table_writer_put8( w, 3 ); + table_writer_put16( w, (uint16_t) ( value->i16_span ) ); + } } - if ( value->i16_low != 0 ) - { - table_writer_put16( w, 0xaaae ); table_writer_put8( w, 3 ); /* i16_low */ - table_writer_put16( w, (uint16_t) ( value->i16_low ) ); + { /* i16_low */ + if ( !( value->i16_low == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd217b3af8d7a2bdeull ); table_writer_put8( w, 3 ); + table_writer_put16( w, (uint16_t) ( value->i16_low ) ); + } } - if ( value->i16_high != 0 ) - { - table_writer_put16( w, 0xe8f2 ); table_writer_put8( w, 3 ); /* i16_high */ - table_writer_put16( w, (uint16_t) ( value->i16_high ) ); + { /* i16_high */ + if ( !( value->i16_high == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x90652f70c9127900ull ); table_writer_put8( w, 3 ); + table_writer_put16( w, (uint16_t) ( value->i16_high ) ); + } } - if ( value->i16_inside != 0 ) - { - table_writer_put16( w, 0x1a0e ); table_writer_put8( w, 3 ); /* i16_inside */ - table_writer_put16( w, (uint16_t) ( value->i16_inside ) ); + { /* i16_inside */ + if ( !( value->i16_inside == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6a659d7c354db158ull ); table_writer_put8( w, 3 ); + table_writer_put16( w, (uint16_t) ( value->i16_inside ) ); + } } - if ( value->i32_span != 0 ) - { - table_writer_put16( w, 0x6bd1 ); table_writer_put8( w, 4 ); /* i32_span */ - table_writer_put32( w, (uint32_t) ( value->i32_span ) ); + { /* i32_span */ + if ( !( value->i32_span == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xe693bd88532c91d6ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->i32_span ) ); + } } - if ( value->i32_low != 0 ) - { - table_writer_put16( w, 0xb8e0 ); table_writer_put8( w, 4 ); /* i32_low */ - table_writer_put32( w, (uint32_t) ( value->i32_low ) ); + { /* i32_low */ + if ( !( value->i32_low == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x065ee827cf99fbb4ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->i32_low ) ); + } } - if ( value->i32_high != 0 ) - { - table_writer_put16( w, 0x3806 ); table_writer_put8( w, 4 ); /* i32_high */ - table_writer_put32( w, (uint32_t) ( value->i32_high ) ); + { /* i32_high */ + if ( !( value->i32_high == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc9b121c4c2a186eeull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->i32_high ) ); + } } - if ( value->i32_inside != 0 ) - { - table_writer_put16( w, 0x9a24 ); table_writer_put8( w, 4 ); /* i32_inside */ - table_writer_put32( w, (uint32_t) ( value->i32_inside ) ); + { /* i32_inside */ + if ( !( value->i32_inside == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd363dfa465acf27aull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->i32_inside ) ); + } } - if ( value->i64_span != 0 ) - { - table_writer_put16( w, 0xf086 ); table_writer_put8( w, 5 ); /* i64_span */ - table_writer_put64( w, (uint64_t) ( value->i64_span ) ); + { /* i64_span */ + if ( !( value->i64_span == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7549c70700c49d6full ); table_writer_put8( w, 5 ); + table_writer_put64( w, (uint64_t) ( value->i64_span ) ); + } } - if ( value->i64_low != 0 ) - { - table_writer_put16( w, 0xc705 ); table_writer_put8( w, 5 ); /* i64_low */ - table_writer_put64( w, (uint64_t) ( value->i64_low ) ); + { /* i64_low */ + if ( !( value->i64_low == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1cce6617419771dbull ); table_writer_put8( w, 5 ); + table_writer_put64( w, (uint64_t) ( value->i64_low ) ); + } } - if ( value->i64_high != 0 ) - { - table_writer_put16( w, 0x328e ); table_writer_put8( w, 5 ); /* i64_high */ - table_writer_put64( w, (uint64_t) ( value->i64_high ) ); + { /* i64_high */ + if ( !( value->i64_high == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x3b54fa60620b5597ull ); table_writer_put8( w, 5 ); + table_writer_put64( w, (uint64_t) ( value->i64_high ) ); + } } - if ( value->i64_inside != 0 ) - { - table_writer_put16( w, 0x7d51 ); table_writer_put8( w, 5 ); /* i64_inside */ - table_writer_put64( w, (uint64_t) ( value->i64_inside ) ); + { /* i64_inside */ + if ( !( value->i64_inside == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd308fcb98ece5d23ull ); table_writer_put8( w, 5 ); + table_writer_put64( w, (uint64_t) ( value->i64_inside ) ); + } } - if ( value->edges_count < 0 || value->edges_count > 4 ) { return 0; } /* storage invariant */ - if ( value->edges_count > 0 ) - { - int64_t len_at_edges; - int32_t i; - table_writer_put16( w, 0xb1cb ); table_writer_put8( w, 14 ); /* edges */ - len_at_edges = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 3 ); table_writer_put32( w, (uint32_t) value->edges_count ); - for ( i = 0; i < value->edges_count; i++ ) + { /* edges */ + if ( value->edges_count < 0 || value->edges_count > 4 ) { return 0; } + if ( value->edges_count > 0 ) { - table_writer_put16( w, (uint16_t) ( value->edges[i] ) ); + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc70cde6b85b6197dull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_ranged_signed_wire_edges_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_ranged_signed_wire_edges_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_ranged_signed_wire_edges_( w, value ) ) { return 0; } + } } - table_writer_patch32( w, len_at_edges, (uint32_t) ( w->offset - len_at_edges - 4 ) ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t ranged_signed_measure( const RangedSigned * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !ranged_signed_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t ranged_signed_save( const RangedSigned * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !ranged_signed_save_body( &w, value ) ) { return -1; } - return w.offset; /* == ranged_signed_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_signed_load_body( TableReader * r, RangedSigned * value ) +static SCHEMA_UNUSED int ranged_signed_load_body( TableReader * r, RangedSigned * value ) { - ranged_signed_reset( value ); /* prefill declared defaults in place, then overlay */ + ranged_signed_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x8d4f: /* i8_span */ + case 0x48121511bf702eb5ull: /* i8_span */ { if ( kind != 2 ) { @@ -861,14 +1696,22 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_signed_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); - value->i8_span = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); + value->i8_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe337: /* i8_low */ + case 0x942bfe3168090f7dull: /* i8_low */ { if ( kind != 2 ) { @@ -876,15 +1719,23 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_signed_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); - if ( decoded_v > 126 ) { decoded_v = 126; r->report->clamped++; } - value->i8_low = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_v > 126 ) { decoded_v = 126; r->report->clamped++; } + value->i8_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x6a5f: /* i8_high */ + case 0xd182acd105b55dd1ull: /* i8_high */ { if ( kind != 2 ) { @@ -892,15 +1743,23 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_signed_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); - if ( decoded_v < -127 ) { decoded_v = -127; r->report->clamped++; } - value->i8_high = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_v < -127 ) { decoded_v = -127; r->report->clamped++; } + value->i8_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x8eee: /* i8_inside */ + case 0xef9ded23c8912a81ull: /* i8_inside */ { if ( kind != 2 ) { @@ -908,208 +1767,948 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_signed_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); - if ( decoded_v < -127 ) { decoded_v = -127; r->report->clamped++; } - else if ( decoded_v > 126 ) { decoded_v = 126; r->report->clamped++; } - value->i8_inside = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_v < -127 ) { decoded_v = -127; r->report->clamped++; } + else if ( decoded_v > 126 ) { decoded_v = 126; r->report->clamped++; } + value->i8_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x0a1e: /* i16_span */ + case 0xb655574ea23760b4ull: /* i16_span */ { if ( kind != 3 ) { + if ( table_kind_widens( kind, 3 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -32768ll ) { decoded_wide = -32768ll; r->report->clamped++; } + if ( decoded_wide > 32767ll ) { decoded_wide = 32767ll; r->report->clamped++; } + value->i16_span = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + value->i16_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); - value->i16_span = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -32768ll ) { decoded_wide = -32768ll; r->report->clamped++; } + if ( decoded_wide > 32767ll ) { decoded_wide = 32767ll; r->report->clamped++; } + value->i16_span = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + value->i16_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xaaae: /* i16_low */ + case 0xd217b3af8d7a2bdeull: /* i16_low */ { if ( kind != 3 ) { - r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } - break; + if ( table_kind_widens( kind, 3 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -32768ll ) { decoded_wide = -32768ll; r->report->clamped++; } + if ( decoded_wide > 32766ll ) { decoded_wide = 32766ll; r->report->clamped++; } + value->i16_low = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 32766 ) { decoded_v = 32766; r->report->clamped++; } + value->i16_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 32766 ) { decoded_v = 32766; r->report->clamped++; } - value->i16_low = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -32768ll ) { decoded_wide = -32768ll; r->report->clamped++; } + if ( decoded_wide > 32766ll ) { decoded_wide = 32766ll; r->report->clamped++; } + value->i16_low = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 32766 ) { decoded_v = 32766; r->report->clamped++; } + value->i16_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe8f2: /* i16_high */ + case 0x90652f70c9127900ull: /* i16_high */ { if ( kind != 3 ) { + if ( table_kind_widens( kind, 3 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -32767ll ) { decoded_wide = -32767ll; r->report->clamped++; } + if ( decoded_wide > 32767ll ) { decoded_wide = 32767ll; r->report->clamped++; } + value->i16_high = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_v < -32767 ) { decoded_v = -32767; r->report->clamped++; } + value->i16_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); - if ( decoded_v < -32767 ) { decoded_v = -32767; r->report->clamped++; } - value->i16_high = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -32767ll ) { decoded_wide = -32767ll; r->report->clamped++; } + if ( decoded_wide > 32767ll ) { decoded_wide = 32767ll; r->report->clamped++; } + value->i16_high = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_v < -32767 ) { decoded_v = -32767; r->report->clamped++; } + value->i16_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x1a0e: /* i16_inside */ + case 0x6a659d7c354db158ull: /* i16_inside */ { if ( kind != 3 ) { + if ( table_kind_widens( kind, 3 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -32767ll ) { decoded_wide = -32767ll; r->report->clamped++; } + if ( decoded_wide > 32766ll ) { decoded_wide = 32766ll; r->report->clamped++; } + value->i16_inside = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_v < -32767 ) { decoded_v = -32767; r->report->clamped++; } + else if ( decoded_v > 32766 ) { decoded_v = 32766; r->report->clamped++; } + value->i16_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); - if ( decoded_v < -32767 ) { decoded_v = -32767; r->report->clamped++; } - else if ( decoded_v > 32766 ) { decoded_v = 32766; r->report->clamped++; } - value->i16_inside = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -32767ll ) { decoded_wide = -32767ll; r->report->clamped++; } + if ( decoded_wide > 32766ll ) { decoded_wide = 32766ll; r->report->clamped++; } + value->i16_inside = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_v < -32767 ) { decoded_v = -32767; r->report->clamped++; } + else if ( decoded_v > 32766 ) { decoded_v = 32766; r->report->clamped++; } + value->i16_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x6bd1: /* i32_span */ + case 0xe693bd88532c91d6ull: /* i32_span */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2147483648ll ) { decoded_wide = -2147483648ll; r->report->clamped++; } + if ( decoded_wide > 2147483647ll ) { decoded_wide = 2147483647ll; r->report->clamped++; } + value->i32_span = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2147483648ll ) { decoded_wide = -2147483648ll; r->report->clamped++; } + if ( decoded_wide > 2147483647ll ) { decoded_wide = 2147483647ll; r->report->clamped++; } + value->i32_span = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + value->i32_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - value->i32_span = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2147483648ll ) { decoded_wide = -2147483648ll; r->report->clamped++; } + if ( decoded_wide > 2147483647ll ) { decoded_wide = 2147483647ll; r->report->clamped++; } + value->i32_span = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2147483648ll ) { decoded_wide = -2147483648ll; r->report->clamped++; } + if ( decoded_wide > 2147483647ll ) { decoded_wide = 2147483647ll; r->report->clamped++; } + value->i32_span = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + value->i32_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xb8e0: /* i32_low */ + case 0x065ee827cf99fbb4ull: /* i32_low */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2147483648ll ) { decoded_wide = -2147483648ll; r->report->clamped++; } + if ( decoded_wide > 2147483646ll ) { decoded_wide = 2147483646ll; r->report->clamped++; } + value->i32_low = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2147483648ll ) { decoded_wide = -2147483648ll; r->report->clamped++; } + if ( decoded_wide > 2147483646ll ) { decoded_wide = 2147483646ll; r->report->clamped++; } + value->i32_low = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v > 2147483646 ) { decoded_v = 2147483646; r->report->clamped++; } + value->i32_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v > 2147483646 ) { decoded_v = 2147483646; r->report->clamped++; } - value->i32_low = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2147483648ll ) { decoded_wide = -2147483648ll; r->report->clamped++; } + if ( decoded_wide > 2147483646ll ) { decoded_wide = 2147483646ll; r->report->clamped++; } + value->i32_low = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2147483648ll ) { decoded_wide = -2147483648ll; r->report->clamped++; } + if ( decoded_wide > 2147483646ll ) { decoded_wide = 2147483646ll; r->report->clamped++; } + value->i32_low = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v > 2147483646 ) { decoded_v = 2147483646; r->report->clamped++; } + value->i32_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x3806: /* i32_high */ + case 0xc9b121c4c2a186eeull: /* i32_high */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2147483647ll ) { decoded_wide = -2147483647ll; r->report->clamped++; } + if ( decoded_wide > 2147483647ll ) { decoded_wide = 2147483647ll; r->report->clamped++; } + value->i32_high = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2147483647ll ) { decoded_wide = -2147483647ll; r->report->clamped++; } + if ( decoded_wide > 2147483647ll ) { decoded_wide = 2147483647ll; r->report->clamped++; } + value->i32_high = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2147483647 ) { decoded_v = -2147483647; r->report->clamped++; } + value->i32_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -2147483647 ) { decoded_v = -2147483647; r->report->clamped++; } - value->i32_high = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2147483647ll ) { decoded_wide = -2147483647ll; r->report->clamped++; } + if ( decoded_wide > 2147483647ll ) { decoded_wide = 2147483647ll; r->report->clamped++; } + value->i32_high = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2147483647ll ) { decoded_wide = -2147483647ll; r->report->clamped++; } + if ( decoded_wide > 2147483647ll ) { decoded_wide = 2147483647ll; r->report->clamped++; } + value->i32_high = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2147483647 ) { decoded_v = -2147483647; r->report->clamped++; } + value->i32_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x9a24: /* i32_inside */ + case 0xd363dfa465acf27aull: /* i32_inside */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2147483647ll ) { decoded_wide = -2147483647ll; r->report->clamped++; } + if ( decoded_wide > 2147483646ll ) { decoded_wide = 2147483646ll; r->report->clamped++; } + value->i32_inside = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2147483647ll ) { decoded_wide = -2147483647ll; r->report->clamped++; } + if ( decoded_wide > 2147483646ll ) { decoded_wide = 2147483646ll; r->report->clamped++; } + value->i32_inside = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2147483647 ) { decoded_v = -2147483647; r->report->clamped++; } + else if ( decoded_v > 2147483646 ) { decoded_v = 2147483646; r->report->clamped++; } + value->i32_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < -2147483647 ) { decoded_v = -2147483647; r->report->clamped++; } - else if ( decoded_v > 2147483646 ) { decoded_v = 2147483646; r->report->clamped++; } - value->i32_inside = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -2147483647ll ) { decoded_wide = -2147483647ll; r->report->clamped++; } + if ( decoded_wide > 2147483646ll ) { decoded_wide = 2147483646ll; r->report->clamped++; } + value->i32_inside = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -2147483647ll ) { decoded_wide = -2147483647ll; r->report->clamped++; } + if ( decoded_wide > 2147483646ll ) { decoded_wide = 2147483646ll; r->report->clamped++; } + value->i32_inside = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < -2147483647 ) { decoded_v = -2147483647; r->report->clamped++; } + else if ( decoded_v > 2147483646 ) { decoded_v = 2147483646; r->report->clamped++; } + value->i32_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xf086: /* i64_span */ + case 0x7549c70700c49d6full: /* i64_span */ { if ( kind != 5 ) { + if ( table_kind_widens( kind, 5 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->i64_span = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->i64_span = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + value->i64_span = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + value->i64_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); - value->i64_span = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->i64_span = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->i64_span = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + value->i64_span = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + value->i64_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xc705: /* i64_low */ + case 0x1cce6617419771dbull: /* i64_low */ { if ( kind != 5 ) { + if ( table_kind_widens( kind, 5 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_low = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_low = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_low = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v > 9223372036854775806ll ) { decoded_v = 9223372036854775806ll; r->report->clamped++; } + value->i64_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); - if ( decoded_v > 9223372036854775806ll ) { decoded_v = 9223372036854775806ll; r->report->clamped++; } - value->i64_low = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_low = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_low = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_low = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v > 9223372036854775806ll ) { decoded_v = 9223372036854775806ll; r->report->clamped++; } + value->i64_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x328e: /* i64_high */ + case 0x3b54fa60620b5597ull: /* i64_high */ { if ( kind != 5 ) { + if ( table_kind_widens( kind, 5 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + value->i64_high = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + value->i64_high = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + value->i64_high = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v < -9223372036854775807ll ) { decoded_v = -9223372036854775807ll; r->report->clamped++; } + value->i64_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); - if ( decoded_v < -9223372036854775807ll ) { decoded_v = -9223372036854775807ll; r->report->clamped++; } - value->i64_high = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + value->i64_high = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + value->i64_high = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + value->i64_high = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v < -9223372036854775807ll ) { decoded_v = -9223372036854775807ll; r->report->clamped++; } + value->i64_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x7d51: /* i64_inside */ + case 0xd308fcb98ece5d23ull: /* i64_inside */ { if ( kind != 5 ) { + if ( table_kind_widens( kind, 5 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_inside = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_inside = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_inside = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v < -9223372036854775807ll ) { decoded_v = -9223372036854775807ll; r->report->clamped++; } + else if ( decoded_v > 9223372036854775806ll ) { decoded_v = 9223372036854775806ll; r->report->clamped++; } + value->i64_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); - if ( decoded_v < -9223372036854775807ll ) { decoded_v = -9223372036854775807ll; r->report->clamped++; } - else if ( decoded_v > 9223372036854775806ll ) { decoded_v = 9223372036854775806ll; r->report->clamped++; } - value->i64_inside = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_inside = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_inside = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < -9223372036854775807ll ) { decoded_wide = -9223372036854775807ll; r->report->clamped++; } + if ( decoded_wide > 9223372036854775806ll ) { decoded_wide = 9223372036854775806ll; r->report->clamped++; } + value->i64_inside = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + if ( decoded_v < -9223372036854775807ll ) { decoded_v = -9223372036854775807ll; r->report->clamped++; } + else if ( decoded_v > 9223372036854775806ll ) { decoded_v = 9223372036854775806ll; r->report->clamped++; } + value->i64_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xb1cb: /* edges */ + case 0xc70cde6b85b6197dull: /* edges */ { if ( kind != 14 ) { @@ -1117,210 +2716,858 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_signed_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 3 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - { - int16_t decoded_v = (int16_t) table_reader_get16( &sub ); - value->edges[i] = decoded_v; - } - decoded = i + 1; - } - value->edges_count = (int32_t) decoded; - } - r->offset = body_end; /* excess elements and slack skip via the length */ + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 3 ) + { + if ( !(table_kind_widens(elem_kind,3)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + int32_t previous_count = value->edges_count; + value->edges_count = 0; + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 2: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_edges; } + int64_t decoded_wide = (int8_t) table_reader_get8( &sub ); + if ( decoded_wide < -32768ll ) { decoded_wide = -32768ll; r->report->clamped++; } + if ( decoded_wide > 32767ll ) { decoded_wide = 32767ll; r->report->clamped++; } + value->edges[i] = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; goto end_edges; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &sub ); + value->edges[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_edges; + } + value->edges_count = (int32_t) i + 1; + } + end_edges: ; + { uint32_t tail; for (tail=(uint32_t)value->edges_count;tail<(uint32_t)previous_count && tail<4;tail++) { + memset(&value->edges[tail],0,sizeof(value->edges[tail])); + } } + } + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int ranged_signed_load( RangedSigned * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + ranged_signed_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return ranged_signed_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t ranged_unsigned_measure( const RangedUnsigned * value ) +static SCHEMA_UNUSED int ranged_signed_save_message_body(TableBitWriter * w,const RangedSigned * value) +{ + (void)value; + { /* i8_span */ + if(!(value->i8_span == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,51,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i8_span)-UINT64_C(18446744073709551488),8); + } + } + { /* i8_low */ + if(!(value->i8_low == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,52,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i8_low)-UINT64_C(18446744073709551488),8); + } + } + { /* i8_high */ + if(!(value->i8_high == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,53,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i8_high)-UINT64_C(18446744073709551489),8); + } + } + { /* i8_inside */ + if(!(value->i8_inside == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,54,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i8_inside)-UINT64_C(18446744073709551489),8); + } + } + { /* i16_span */ + if(!(value->i16_span == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,55,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i16_span)-UINT64_C(18446744073709518848),16); + } + } + { /* i16_low */ + if(!(value->i16_low == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,56,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i16_low)-UINT64_C(18446744073709518848),16); + } + } + { /* i16_high */ + if(!(value->i16_high == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,57,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i16_high)-UINT64_C(18446744073709518849),16); + } + } + { /* i16_inside */ + if(!(value->i16_inside == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,58,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i16_inside)-UINT64_C(18446744073709518849),16); + } + } + { /* i32_span */ + if(!(value->i32_span == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,59,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i32_span)-UINT64_C(18446744071562067968),32); + } + } + { /* i32_low */ + if(!(value->i32_low == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,60,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i32_low)-UINT64_C(18446744071562067968),32); + } + } + { /* i32_high */ + if(!(value->i32_high == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,61,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i32_high)-UINT64_C(18446744071562067969),32); + } + } + { /* i32_inside */ + if(!(value->i32_inside == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,62,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i32_inside)-UINT64_C(18446744071562067969),32); + } + } + { /* i64_span */ + if(!(value->i64_span == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,63,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i64_span)-UINT64_C(9223372036854775808),64); + } + } + { /* i64_low */ + if(!(value->i64_low == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,64,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i64_low)-UINT64_C(9223372036854775808),64); + } + } + { /* i64_high */ + if(!(value->i64_high == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,65,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i64_high)-UINT64_C(9223372036854775809),64); + } + } + { /* i64_inside */ + if(!(value->i64_inside == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,66,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->i64_inside)-UINT64_C(9223372036854775809),64); + } + } + { /* edges */ + if(value->edges_count<0 || value->edges_count>4) return 0; + if(value->edges_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,67,kTableMessageRefBitsHere); + if(value->edges_count<0 || value->edges_count>4) return 0; + table_bit_put(w,(uint64_t)value->edges_count-0,3); + { int32_t i; + for(i=0;iedges_count;i++) { + table_bit_put(w,(uint64_t)(value->edges[i])-UINT64_C(18446744073709518848),16); + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t ranged_signed_measure_messages(const RangedSigned * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x48121511bf702eb5): { + if(!(entry->kind==2 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,2)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-128ll){decoded=-128ll; + r->report->clamped++; + } + if(decoded>127ll){decoded=127ll; + r->report->clamped++; + } + value->i8_span=(int8_t)decoded; + } + break; + } + case UINT64_C(0x942bfe3168090f7d): { + if(!(entry->kind==2 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,2)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-128ll){decoded=-128ll; + r->report->clamped++; + } + if(decoded>126ll){decoded=126ll; + r->report->clamped++; + } + value->i8_low=(int8_t)decoded; + } + break; + } + case UINT64_C(0xd182acd105b55dd1): { + if(!(entry->kind==2 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,2)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-127ll){decoded=-127ll; + r->report->clamped++; + } + if(decoded>127ll){decoded=127ll; + r->report->clamped++; + } + value->i8_high=(int8_t)decoded; + } + break; + } + case UINT64_C(0xef9ded23c8912a81): { + if(!(entry->kind==2 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,2)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-127ll){decoded=-127ll; + r->report->clamped++; + } + if(decoded>126ll){decoded=126ll; + r->report->clamped++; + } + value->i8_inside=(int8_t)decoded; + } + break; + } + case UINT64_C(0xb655574ea23760b4): { + if(!(entry->kind==3 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,3)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-32768ll){decoded=-32768ll; + r->report->clamped++; + } + if(decoded>32767ll){decoded=32767ll; + r->report->clamped++; + } + value->i16_span=(int16_t)decoded; + } + break; + } + case UINT64_C(0xd217b3af8d7a2bde): { + if(!(entry->kind==3 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,3)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-32768ll){decoded=-32768ll; + r->report->clamped++; + } + if(decoded>32766ll){decoded=32766ll; + r->report->clamped++; + } + value->i16_low=(int16_t)decoded; + } + break; + } + case UINT64_C(0x90652f70c9127900): { + if(!(entry->kind==3 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,3)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-32767ll){decoded=-32767ll; + r->report->clamped++; + } + if(decoded>32767ll){decoded=32767ll; + r->report->clamped++; + } + value->i16_high=(int16_t)decoded; + } + break; + } + case UINT64_C(0x6a659d7c354db158): { + if(!(entry->kind==3 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,3)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-32767ll){decoded=-32767ll; + r->report->clamped++; + } + if(decoded>32766ll){decoded=32766ll; + r->report->clamped++; + } + value->i16_inside=(int16_t)decoded; + } + break; + } + case UINT64_C(0xe693bd88532c91d6): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2147483648ll){decoded=-2147483648ll; + r->report->clamped++; + } + if(decoded>2147483647ll){decoded=2147483647ll; + r->report->clamped++; + } + value->i32_span=(int32_t)decoded; + } + break; + } + case UINT64_C(0x065ee827cf99fbb4): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2147483648ll){decoded=-2147483648ll; + r->report->clamped++; + } + if(decoded>2147483646ll){decoded=2147483646ll; + r->report->clamped++; + } + value->i32_low=(int32_t)decoded; + } + break; + } + case UINT64_C(0xc9b121c4c2a186ee): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2147483647ll){decoded=-2147483647ll; + r->report->clamped++; + } + if(decoded>2147483647ll){decoded=2147483647ll; + r->report->clamped++; + } + value->i32_high=(int32_t)decoded; + } + break; + } + case UINT64_C(0xd363dfa465acf27a): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2147483647ll){decoded=-2147483647ll; + r->report->clamped++; + } + if(decoded>2147483646ll){decoded=2147483646ll; + r->report->clamped++; + } + value->i32_inside=(int32_t)decoded; + } + break; + } + case UINT64_C(0x7549c70700c49d6f): { + if(!(entry->kind==5 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,5)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + value->i64_span=(int64_t)decoded; + } + break; + } + case UINT64_C(0x1cce6617419771db): { + if(!(entry->kind==5 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,5)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded>9223372036854775806ll){decoded=9223372036854775806ll; + r->report->clamped++; + } + value->i64_low=(int64_t)decoded; + } + break; + } + case UINT64_C(0x3b54fa60620b5597): { + if(!(entry->kind==5 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,5)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-9223372036854775807ll){decoded=-9223372036854775807ll; + r->report->clamped++; + } + value->i64_high=(int64_t)decoded; + } + break; + } + case UINT64_C(0xd308fcb98ece5d23): { + if(!(entry->kind==5 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,5)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-9223372036854775807ll){decoded=-9223372036854775807ll; + r->report->clamped++; + } + if(decoded>9223372036854775806ll){decoded=9223372036854775806ll; + r->report->clamped++; + } + value->i64_inside=(int64_t)decoded; + } + break; + } + case UINT64_C(0xc70cde6b85b6197d): { + if(!(entry->kind==14 && entry->elem_kind==3)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,3)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iedges[i] : &scratch; + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-32768ll){decoded=-32768ll; + r->report->clamped++; + } + if(decoded>32767ll){decoded=32767ll; + r->report->clamped++; + } + (*item)=(int16_t)decoded; + } + }if(walkvalue_bits))goto malformed; + value->edges_count=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int ranged_signed_load_messages(RangedSigned * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_tabledemo_ranged_unsigned_wire_counts_( TableWriter * w, const RangedUnsigned * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->u8_span != 0 ) { bytes += 3 + 1; } /* u8_span */ - if ( value->u8_low != 0 ) { bytes += 3 + 1; } /* u8_low */ - if ( value->u8_high != 1 ) { bytes += 3 + 1; } /* u8_high */ - if ( value->u8_inside != 1 ) { bytes += 3 + 1; } /* u8_inside */ - if ( value->u16_span != 0 ) { bytes += 3 + 2; } /* u16_span */ - if ( value->u16_low != 0 ) { bytes += 3 + 2; } /* u16_low */ - if ( value->u16_high != 1 ) { bytes += 3 + 2; } /* u16_high */ - if ( value->u16_inside != 1 ) { bytes += 3 + 2; } /* u16_inside */ - if ( value->u32_span != 0 ) { bytes += 3 + 4; } /* u32_span */ - if ( value->u32_low != 0 ) { bytes += 3 + 4; } /* u32_low */ - if ( value->u32_high != 1 ) { bytes += 3 + 4; } /* u32_high */ - if ( value->u32_inside != 1 ) { bytes += 3 + 4; } /* u32_inside */ - if ( value->u64_span != 0 ) { bytes += 3 + 8; } /* u64_span */ - if ( value->u64_low != 0 ) { bytes += 3 + 8; } /* u64_low */ - if ( value->u64_high != 1ull ) { bytes += 3 + 8; } /* u64_high */ - if ( value->u64_inside != 1ull ) { bytes += 3 + 8; } /* u64_inside */ - if ( value->counts_count < 0 || value->counts_count > 4 ) { return -1; } /* storage invariant */ - if ( value->counts_count > 0 ) + if ( value->counts_count < 0 || value->counts_count > 4 ) { return 0; } + int32_t i; + table_writer_put8( w, 9 ); table_writer_leb( w, (uint64_t) value->counts_count ); + for ( i = 0; i < value->counts_count; i++ ) { - bytes += 3 + 4 + 5 + (int64_t) value->counts_count * 8; /* counts */ + table_writer_put64( w, (uint64_t) ( value->counts[i] ) ); } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_unsigned_save_body( TableWriter * w, const RangedUnsigned * value ) +static SCHEMA_UNUSED int ranged_unsigned_save_body( TableWriter * w, const RangedUnsigned * value ) { - if ( value->u8_span != 0 ) - { - table_writer_put16( w, 0x6434 ); table_writer_put8( w, 6 ); /* u8_span */ - table_writer_put8( w, (uint8_t) ( value->u8_span ) ); + (void) value; + { /* u8_span */ + if ( !( value->u8_span == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0f8897557f37c021ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->u8_span ) ); + } } - if ( value->u8_low != 0 ) - { - table_writer_put16( w, 0xb24e ); table_writer_put8( w, 6 ); /* u8_low */ - table_writer_put8( w, (uint8_t) ( value->u8_low ) ); + { /* u8_low */ + if ( !( value->u8_low == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x2e17553f8200a2a1ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->u8_low ) ); + } } - if ( value->u8_high != 1 ) - { - table_writer_put16( w, 0x202a ); table_writer_put8( w, 6 ); /* u8_high */ - table_writer_put8( w, (uint8_t) ( value->u8_high ) ); + { /* u8_high */ + if ( !( value->u8_high == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa0e5c60df9301b7dull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->u8_high ) ); + } } - if ( value->u8_inside != 1 ) - { - table_writer_put16( w, 0x8fbf ); table_writer_put8( w, 6 ); /* u8_inside */ - table_writer_put8( w, (uint8_t) ( value->u8_inside ) ); + { /* u8_inside */ + if ( !( value->u8_inside == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1cb2c073a6f5844dull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->u8_inside ) ); + } } - if ( value->u16_span != 0 ) - { - table_writer_put16( w, 0xd410 ); table_writer_put8( w, 7 ); /* u16_span */ - table_writer_put16( w, (uint16_t) ( value->u16_span ) ); + { /* u16_span */ + if ( !( value->u16_span == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4ca0c150b1790960ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->u16_span ) ); + } } - if ( value->u16_low != 0 ) - { - table_writer_put16( w, 0x6f72 ); table_writer_put8( w, 7 ); /* u16_low */ - table_writer_put16( w, (uint16_t) ( value->u16_low ) ); + { /* u16_low */ + if ( !( value->u16_low == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xf252136836b04cb2ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->u16_low ) ); + } } - if ( value->u16_high != 1 ) - { - table_writer_put16( w, 0x1364 ); table_writer_put8( w, 7 ); /* u16_high */ - table_writer_put16( w, (uint16_t) ( value->u16_high ) ); + { /* u16_high */ + if ( !( value->u16_high == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4f37e1f216e7610cull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->u16_high ) ); + } } - if ( value->u16_inside != 1 ) - { - table_writer_put16( w, 0xbf32 ); table_writer_put8( w, 7 ); /* u16_inside */ - table_writer_put16( w, (uint16_t) ( value->u16_inside ) ); + { /* u16_inside */ + if ( !( value->u16_inside == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd176b605978f3304ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->u16_inside ) ); + } } - if ( value->u32_span != 0 ) - { - table_writer_put16( w, 0x0513 ); table_writer_put8( w, 8 ); /* u32_span */ - table_writer_put32( w, (uint32_t) ( value->u32_span ) ); + { /* u32_span */ + if ( !( value->u32_span == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x200b924de7479cdaull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->u32_span ) ); + } } - if ( value->u32_low != 0 ) - { - table_writer_put16( w, 0x4cce ); table_writer_put8( w, 8 ); /* u32_low */ - table_writer_put32( w, (uint32_t) ( value->u32_low ) ); + { /* u32_low */ + if ( !( value->u32_low == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa53d2f2a31b5acb0ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->u32_low ) ); + } } - if ( value->u32_high != 1 ) - { - table_writer_put16( w, 0xa2da ); table_writer_put8( w, 8 ); /* u32_high */ - table_writer_put32( w, (uint32_t) ( value->u32_high ) ); + { /* u32_high */ + if ( !( value->u32_high == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x9759be8ea1a4e3d2ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->u32_high ) ); + } } - if ( value->u32_inside != 1 ) - { - table_writer_put16( w, 0xf596 ); table_writer_put8( w, 8 ); /* u32_inside */ - table_writer_put32( w, (uint32_t) ( value->u32_inside ) ); + { /* u32_inside */ + if ( !( value->u32_inside == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x8dc515fc082e3c0eull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->u32_inside ) ); + } } - if ( value->u64_span != 0 ) - { - table_writer_put16( w, 0x6e8d ); table_writer_put8( w, 9 ); /* u64_span */ - table_writer_put64( w, (uint64_t) ( value->u64_span ) ); + { /* u64_span */ + if ( !( value->u64_span == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbeecef11dbdf8973ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->u64_span ) ); + } } - if ( value->u64_low != 0 ) - { - table_writer_put16( w, 0x95b5 ); table_writer_put8( w, 9 ); /* u64_low */ - table_writer_put64( w, (uint64_t) ( value->u64_low ) ); + { /* u64_low */ + if ( !( value->u64_low == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0117ad66e0fff227ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->u64_low ) ); + } } - if ( value->u64_high != 1ull ) - { - table_writer_put16( w, 0x15e4 ); table_writer_put8( w, 9 ); /* u64_high */ - table_writer_put64( w, (uint64_t) ( value->u64_high ) ); + { /* u64_high */ + if ( !( value->u64_high == 1ull ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xf2b45af3b50689dbull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->u64_high ) ); + } } - if ( value->u64_inside != 1ull ) - { - table_writer_put16( w, 0x2ce0 ); table_writer_put8( w, 9 ); /* u64_inside */ - table_writer_put64( w, (uint64_t) ( value->u64_inside ) ); + { /* u64_inside */ + if ( !( value->u64_inside == 1ull ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x713e12017eebcaf7ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->u64_inside ) ); + } } - if ( value->counts_count < 0 || value->counts_count > 4 ) { return 0; } /* storage invariant */ - if ( value->counts_count > 0 ) - { - int64_t len_at_counts; - int32_t i; - table_writer_put16( w, 0xe27a ); table_writer_put8( w, 14 ); /* counts */ - len_at_counts = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 9 ); table_writer_put32( w, (uint32_t) value->counts_count ); - for ( i = 0; i < value->counts_count; i++ ) + { /* counts */ + if ( value->counts_count < 0 || value->counts_count > 4 ) { return 0; } + if ( value->counts_count > 0 ) { - table_writer_put64( w, (uint64_t) ( value->counts[i] ) ); + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc341febe5aae51e5ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_ranged_unsigned_wire_counts_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_ranged_unsigned_wire_counts_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_ranged_unsigned_wire_counts_( w, value ) ) { return 0; } + } } - table_writer_patch32( w, len_at_counts, (uint32_t) ( w->offset - len_at_counts - 4 ) ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t ranged_unsigned_measure( const RangedUnsigned * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !ranged_unsigned_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t ranged_unsigned_save( const RangedUnsigned * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !ranged_unsigned_save_body( &w, value ) ) { return -1; } - return w.offset; /* == ranged_unsigned_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_unsigned_load_body( TableReader * r, RangedUnsigned * value ) +static SCHEMA_UNUSED int ranged_unsigned_load_body( TableReader * r, RangedUnsigned * value ) { - ranged_unsigned_reset( value ); /* prefill declared defaults in place, then overlay */ + ranged_unsigned_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x6434: /* u8_span */ + case 0x0f8897557f37c021ull: /* u8_span */ { if ( kind != 6 ) { @@ -1328,14 +3575,22 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_unsigned_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->u8_span = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->u8_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xb24e: /* u8_low */ + case 0x2e17553f8200a2a1ull: /* u8_low */ { if ( kind != 6 ) { @@ -1343,15 +3598,23 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_unsigned_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - if ( decoded_v > 254 ) { decoded_v = 254; r->report->clamped++; } - value->u8_low = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_v > 254 ) { decoded_v = 254; r->report->clamped++; } + value->u8_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x202a: /* u8_high */ + case 0xa0e5c60df9301b7dull: /* u8_high */ { if ( kind != 6 ) { @@ -1359,15 +3622,23 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_unsigned_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } - value->u8_high = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + value->u8_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x8fbf: /* u8_inside */ + case 0x1cb2c073a6f5844dull: /* u8_inside */ { if ( kind != 6 ) { @@ -1375,208 +3646,936 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_unsigned_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } - else if ( decoded_v > 254 ) { decoded_v = 254; r->report->clamped++; } - value->u8_inside = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + else if ( decoded_v > 254 ) { decoded_v = 254; r->report->clamped++; } + value->u8_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xd410: /* u16_span */ + case 0x4ca0c150b1790960ull: /* u16_span */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 65535ull ) { decoded_wide = 65535ull; r->report->clamped++; } + value->u16_span = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->u16_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - value->u16_span = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 65535ull ) { decoded_wide = 65535ull; r->report->clamped++; } + value->u16_span = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->u16_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x6f72: /* u16_low */ + case 0xf252136836b04cb2ull: /* u16_low */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 65534ull ) { decoded_wide = 65534ull; r->report->clamped++; } + value->u16_low = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 65534 ) { decoded_v = 65534; r->report->clamped++; } + value->u16_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 65534 ) { decoded_v = 65534; r->report->clamped++; } - value->u16_low = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 65534ull ) { decoded_wide = 65534ull; r->report->clamped++; } + value->u16_low = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 65534 ) { decoded_v = 65534; r->report->clamped++; } + value->u16_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x1364: /* u16_high */ + case 0x4f37e1f216e7610cull: /* u16_high */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 65535ull ) { decoded_wide = 65535ull; r->report->clamped++; } + value->u16_high = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + value->u16_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } - value->u16_high = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 65535ull ) { decoded_wide = 65535ull; r->report->clamped++; } + value->u16_high = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + value->u16_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xbf32: /* u16_inside */ + case 0xd176b605978f3304ull: /* u16_inside */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 65534ull ) { decoded_wide = 65534ull; r->report->clamped++; } + value->u16_inside = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + else if ( decoded_v > 65534 ) { decoded_v = 65534; r->report->clamped++; } + value->u16_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } - else if ( decoded_v > 65534 ) { decoded_v = 65534; r->report->clamped++; } - value->u16_inside = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 65534ull ) { decoded_wide = 65534ull; r->report->clamped++; } + value->u16_inside = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + else if ( decoded_v > 65534 ) { decoded_v = 65534; r->report->clamped++; } + value->u16_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x0513: /* u32_span */ + case 0x200b924de7479cdaull: /* u32_span */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->u32_span = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->u32_span = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->u32_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->u32_span = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->u32_span = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->u32_span = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->u32_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x4cce: /* u32_low */ + case 0xa53d2f2a31b5acb0ull: /* u32_low */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4294967294ull ) { decoded_wide = 4294967294ull; r->report->clamped++; } + value->u32_low = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 4294967294ull ) { decoded_wide = 4294967294ull; r->report->clamped++; } + value->u32_low = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v > 4294967294 ) { decoded_v = 4294967294; r->report->clamped++; } + value->u32_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - if ( decoded_v > 4294967294 ) { decoded_v = 4294967294; r->report->clamped++; } - value->u32_low = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4294967294ull ) { decoded_wide = 4294967294ull; r->report->clamped++; } + value->u32_low = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 4294967294ull ) { decoded_wide = 4294967294ull; r->report->clamped++; } + value->u32_low = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v > 4294967294 ) { decoded_v = 4294967294; r->report->clamped++; } + value->u32_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xa2da: /* u32_high */ + case 0x9759be8ea1a4e3d2ull: /* u32_high */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->u32_high = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->u32_high = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + value->u32_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } - value->u32_high = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->u32_high = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->u32_high = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + value->u32_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xf596: /* u32_inside */ + case 0x8dc515fc082e3c0eull: /* u32_inside */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 4294967294ull ) { decoded_wide = 4294967294ull; r->report->clamped++; } + value->u32_inside = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 4294967294ull ) { decoded_wide = 4294967294ull; r->report->clamped++; } + value->u32_inside = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + else if ( decoded_v > 4294967294 ) { decoded_v = 4294967294; r->report->clamped++; } + value->u32_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } - else if ( decoded_v > 4294967294 ) { decoded_v = 4294967294; r->report->clamped++; } - value->u32_inside = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 4294967294ull ) { decoded_wide = 4294967294ull; r->report->clamped++; } + value->u32_inside = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 4294967294ull ) { decoded_wide = 4294967294ull; r->report->clamped++; } + value->u32_inside = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 1 ) { decoded_v = 1; r->report->clamped++; } + else if ( decoded_v > 4294967294 ) { decoded_v = 4294967294; r->report->clamped++; } + value->u32_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x6e8d: /* u64_span */ + case 0xbeecef11dbdf8973ull: /* u64_span */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->u64_span = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->u64_span = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->u64_span = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->u64_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->u64_span = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->u64_span = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->u64_span = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->u64_span = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->u64_span = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x95b5: /* u64_low */ + case 0x0117ad66e0fff227ull: /* u64_low */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_low = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_low = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_low = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v > 18446744073709551614ull ) { decoded_v = 18446744073709551614ull; r->report->clamped++; } + value->u64_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - if ( decoded_v > 18446744073709551614ull ) { decoded_v = 18446744073709551614ull; r->report->clamped++; } - value->u64_low = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_low = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_low = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_low = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v > 18446744073709551614ull ) { decoded_v = 18446744073709551614ull; r->report->clamped++; } + value->u64_low = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x15e4: /* u64_high */ + case 0xf2b45af3b50689dbull: /* u64_high */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + value->u64_high = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + value->u64_high = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + value->u64_high = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } + value->u64_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } - value->u64_high = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + value->u64_high = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + value->u64_high = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + value->u64_high = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } + value->u64_high = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x2ce0: /* u64_inside */ + case 0x713e12017eebcaf7ull: /* u64_inside */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_inside = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_inside = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_inside = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } + else if ( decoded_v > 18446744073709551614ull ) { decoded_v = 18446744073709551614ull; r->report->clamped++; } + value->u64_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } - else if ( decoded_v > 18446744073709551614ull ) { decoded_v = 18446744073709551614ull; r->report->clamped++; } - value->u64_inside = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_inside = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_inside = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide < 1ull ) { decoded_wide = 1ull; r->report->clamped++; } + if ( decoded_wide > 18446744073709551614ull ) { decoded_wide = 18446744073709551614ull; r->report->clamped++; } + value->u64_inside = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v < 1ull ) { decoded_v = 1ull; r->report->clamped++; } + else if ( decoded_v > 18446744073709551614ull ) { decoded_v = 18446744073709551614ull; r->report->clamped++; } + value->u64_inside = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe27a: /* counts */ + case 0xc341febe5aae51e5ull: /* counts */ { if ( kind != 14 ) { @@ -1584,131 +4583,733 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_unsigned_load_body if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 9 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) - { - if ( !table_reader_has( &sub, 8 ) ) { r->report->malformed = 1; break; } - { - uint64_t decoded_v = (uint64_t) table_reader_get64( &sub ); - value->counts[i] = decoded_v; - } - decoded = i + 1; - } - value->counts_count = (int32_t) decoded; - } - r->offset = body_end; /* excess elements and slack skip via the length */ + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else + { + if ( elem_kind != 9 ) + { + if ( !(table_kind_widens(elem_kind,9)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + int32_t previous_count = value->counts_count; + value->counts_count = 0; + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 6: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_counts; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &sub ); + value->counts[i] = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; goto end_counts; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &sub ); + value->counts[i] = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; goto end_counts; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &sub ); + value->counts[i] = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &sub, 8 ) ) { r->report->malformed = 1; goto end_counts; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &sub ); + value->counts[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_counts; + } + value->counts_count = (int32_t) i + 1; + } + end_counts: ; + { uint32_t tail; for (tail=(uint32_t)value->counts_count;tail<(uint32_t)previous_count && tail<4;tail++) { + memset(&value->counts[tail],0,sizeof(value->counts[tail])); + } } + } + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int ranged_unsigned_load( RangedUnsigned * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + ranged_unsigned_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return ranged_unsigned_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t ranged_widths_measure( const RangedWidths * value ) +static SCHEMA_UNUSED int ranged_unsigned_save_message_body(TableBitWriter * w,const RangedUnsigned * value) { - int64_t bytes = 2; /* terminator */ - if ( value->b8 != 0 ) { bytes += 3 + 1; } /* b8 */ - if ( value->b16 != 0 ) { bytes += 3 + 2; } /* b16 */ - if ( value->b32 != 0 ) { bytes += 3 + 4; } /* b32 */ - if ( value->b64 != 0 ) { bytes += 3 + 8; } /* b64 */ - if ( value->b12 != 0 ) { bytes += 3 + 2; } /* b12 */ - if ( value->b48 != 0 ) { bytes += 3 + 8; } /* b48 */ - return bytes; + (void)value; + { /* u8_span */ + if(!(value->u8_span == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,68,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u8_span)-UINT64_C(0),8); + } + } + { /* u8_low */ + if(!(value->u8_low == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,69,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u8_low)-UINT64_C(0),8); + } + } + { /* u8_high */ + if(!(value->u8_high == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,70,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u8_high)-UINT64_C(1),8); + } + } + { /* u8_inside */ + if(!(value->u8_inside == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,71,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u8_inside)-UINT64_C(1),8); + } + } + { /* u16_span */ + if(!(value->u16_span == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,72,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u16_span)-UINT64_C(0),16); + } + } + { /* u16_low */ + if(!(value->u16_low == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,73,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u16_low)-UINT64_C(0),16); + } + } + { /* u16_high */ + if(!(value->u16_high == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,74,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u16_high)-UINT64_C(1),16); + } + } + { /* u16_inside */ + if(!(value->u16_inside == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,75,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u16_inside)-UINT64_C(1),16); + } + } + { /* u32_span */ + if(!(value->u32_span == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,76,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u32_span)-UINT64_C(0),32); + } + } + { /* u32_low */ + if(!(value->u32_low == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,77,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u32_low)-UINT64_C(0),32); + } + } + { /* u32_high */ + if(!(value->u32_high == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,78,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u32_high)-UINT64_C(1),32); + } + } + { /* u32_inside */ + if(!(value->u32_inside == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,79,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u32_inside)-UINT64_C(1),32); + } + } + { /* u64_span */ + if(!(value->u64_span == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,80,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u64_span)-UINT64_C(0),64); + } + } + { /* u64_low */ + if(!(value->u64_low == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,81,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u64_low)-UINT64_C(0),64); + } + } + { /* u64_high */ + if(!(value->u64_high == 1ull)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,82,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u64_high)-UINT64_C(1),64); + } + } + { /* u64_inside */ + if(!(value->u64_inside == 1ull)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,83,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->u64_inside)-UINT64_C(1),64); + } + } + { /* counts */ + if(value->counts_count<0 || value->counts_count>4) return 0; + if(value->counts_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,84,kTableMessageRefBitsHere); + if(value->counts_count<0 || value->counts_count>4) return 0; + table_bit_put(w,(uint64_t)value->counts_count-0,3); + { int32_t i; + for(i=0;icounts_count;i++) { + table_bit_put(w,(uint64_t)(value->counts[i])-UINT64_C(0),64); + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_widths_save_body( TableWriter * w, const RangedWidths * value ) +static SCHEMA_UNUSED int64_t ranged_unsigned_measure_messages(const RangedUnsigned * values,int64_t count,TableReport * report) { - if ( value->b8 != 0 ) - { - table_writer_put16( w, 0xa7cb ); table_writer_put8( w, 6 ); /* b8 */ - table_writer_put8( w, (uint8_t) ( value->b8 ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x0f8897557f37c021): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->u8_span=(uint8_t)decoded; + } + break; + } + case UINT64_C(0x2e17553f8200a2a1): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>254ull){decoded=254ull; + r->report->clamped++; + } + value->u8_low=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xa0e5c60df9301b7d): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->u8_high=(uint8_t)decoded; + } + break; + } + case UINT64_C(0x1cb2c073a6f5844d): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>254ull){decoded=254ull; + r->report->clamped++; + } + value->u8_inside=(uint8_t)decoded; + } + break; + } + case UINT64_C(0x4ca0c150b1790960): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>65535ull){decoded=65535ull; + r->report->clamped++; + } + value->u16_span=(uint16_t)decoded; + } + break; + } + case UINT64_C(0xf252136836b04cb2): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>65534ull){decoded=65534ull; + r->report->clamped++; + } + value->u16_low=(uint16_t)decoded; + } + break; + } + case UINT64_C(0x4f37e1f216e7610c): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>65535ull){decoded=65535ull; + r->report->clamped++; + } + value->u16_high=(uint16_t)decoded; + } + break; + } + case UINT64_C(0xd176b605978f3304): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>65534ull){decoded=65534ull; + r->report->clamped++; + } + value->u16_inside=(uint16_t)decoded; + } + break; + } + case UINT64_C(0x200b924de7479cda): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->u32_span=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xa53d2f2a31b5acb0): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967294ull){decoded=4294967294ull; + r->report->clamped++; + } + value->u32_low=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x9759be8ea1a4e3d2): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->u32_high=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x8dc515fc082e3c0e): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>4294967294ull){decoded=4294967294ull; + r->report->clamped++; + } + value->u32_inside=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xbeecef11dbdf8973): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->u64_span=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x0117ad66e0fff227): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>18446744073709551614ull){decoded=18446744073709551614ull; + r->report->clamped++; + } + value->u64_low=(uint64_t)decoded; + } + break; + } + case UINT64_C(0xf2b45af3b50689db): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + value->u64_high=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x713e12017eebcaf7): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded<1ull){decoded=1ull; + r->report->clamped++; + } + if(decoded>18446744073709551614ull){decoded=18446744073709551614ull; + r->report->clamped++; + } + value->u64_inside=(uint64_t)decoded; + } + break; + } + case UINT64_C(0xc341febe5aae51e5): { + if(!(entry->kind==14 && entry->elem_kind==9)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;icounts[i] : &scratch; + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + (*item)=(uint64_t)decoded; + } + }if(walkvalue_bits))goto malformed; + value->counts_count=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int ranged_unsigned_load_messages(RangedUnsigned * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int ranged_widths_save_body( TableWriter * w, const RangedWidths * value ) +{ + (void) value; + { /* b8 */ + if ( !( value->b8 == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x08a60c07b54d8dc7ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->b8 ) ); + } } - if ( value->b16 != 0 ) - { - table_writer_put16( w, 0xb643 ); table_writer_put8( w, 7 ); /* b16 */ - table_writer_put16( w, (uint16_t) ( value->b16 ) ); + { /* b16 */ + if ( !( value->b16 == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xff95701912ad97beull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->b16 ) ); + } } - if ( value->b32 != 0 ) - { - table_writer_put16( w, 0x33f2 ); table_writer_put8( w, 8 ); /* b32 */ - table_writer_put32( w, (uint32_t) ( value->b32 ) ); + { /* b32 */ + if ( !( value->b32 == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xff9c5c1912b39470ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->b32 ) ); + } } - if ( value->b64 != 0 ) - { - table_writer_put16( w, 0x75dc ); table_writer_put8( w, 9 ); /* b64 */ - table_writer_put64( w, (uint64_t) ( value->b64 ) ); + { /* b64 */ + if ( !( value->b64 == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xff92701912ab61e7ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->b64 ) ); + } } - if ( value->b12 != 0 ) - { - table_writer_put16( w, 0xbcf7 ); table_writer_put8( w, 7 ); /* b12 */ - table_writer_put16( w, (uint16_t) ( value->b12 ) ); + { /* b12 */ + if ( !( value->b12 == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xff95741912ad9e8aull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->b12 ) ); + } } - if ( value->b48 != 0 ) - { - table_writer_put16( w, 0x9797 ); table_writer_put8( w, 9 ); /* b48 */ - table_writer_put64( w, (uint64_t) ( value->b48 ) ); + { /* b48 */ + if ( !( value->b48 == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xff8b681912a535a1ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->b48 ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t ranged_widths_measure( const RangedWidths * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !ranged_widths_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t ranged_widths_save( const RangedWidths * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !ranged_widths_save_body( &w, value ) ) { return -1; } - return w.offset; /* == ranged_widths_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_widths_load_body( TableReader * r, RangedWidths * value ) +static SCHEMA_UNUSED int ranged_widths_load_body( TableReader * r, RangedWidths * value ) { - ranged_widths_reset( value ); /* prefill declared defaults in place, then overlay */ + ranged_widths_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xa7cb: /* b8 */ + case 0x08a60c07b54d8dc7ull: /* b8 */ { if ( kind != 6 ) { @@ -1716,109 +5317,668 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int ranged_widths_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->b8 = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->b8 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xb643: /* b16 */ + case 0xff95701912ad97beull: /* b16 */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 65535ull ) { decoded_wide = 65535ull; r->report->clamped++; } + value->b16 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->b16 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - value->b16 = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 65535ull ) { decoded_wide = 65535ull; r->report->clamped++; } + value->b16 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->b16 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x33f2: /* b32 */ + case 0xff9c5c1912b39470ull: /* b32 */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->b32 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->b32 = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->b32 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->b32 = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->b32 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 4294967295ull ) { decoded_wide = 4294967295ull; r->report->clamped++; } + value->b32 = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->b32 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x75dc: /* b64 */ + case 0xff92701912ab61e7ull: /* b64 */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->b64 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->b64 = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->b64 = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->b64 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->b64 = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->b64 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->b64 = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->b64 = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->b64 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xbcf7: /* b12 */ + case 0xff95741912ad9e8aull: /* b12 */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4095ull ) { decoded_wide = 4095ull; r->report->clamped++; } + value->b12 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ + value->b12 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ - value->b12 = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 4095ull ) { decoded_wide = 4095ull; r->report->clamped++; } + value->b12 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_v > 4095ull ) { decoded_v = 4095ull; r->report->clamped++; } /* bits(12) width clamp */ + value->b12 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x9797: /* b48 */ + case 0xff8b681912a535a1ull: /* b48 */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->b48 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->b48 = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->b48 = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v > 281474976710655ull ) { decoded_v = 281474976710655ull; r->report->clamped++; } /* bits(48) width clamp */ + value->b48 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - if ( decoded_v > 281474976710655ull ) { decoded_v = 281474976710655ull; r->report->clamped++; } /* bits(48) width clamp */ - value->b48 = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->b48 = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->b48 = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + if ( decoded_wide > 281474976710655ull ) { decoded_wide = 281474976710655ull; r->report->clamped++; } + value->b48 = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + if ( decoded_v > 281474976710655ull ) { decoded_v = 281474976710655ull; r->report->clamped++; } /* bits(48) width clamp */ + value->b48 = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int ranged_widths_load( RangedWidths * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + ranged_widths_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return ranged_widths_load_body( &r, value ); } +static SCHEMA_UNUSED int ranged_widths_save_message_body(TableBitWriter * w,const RangedWidths * value) +{ + (void)value; + { /* b8 */ + if(!(value->b8 == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,85,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->b8)-UINT64_C(0),8); + } + } + { /* b16 */ + if(!(value->b16 == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,86,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->b16)-UINT64_C(0),16); + } + } + { /* b32 */ + if(!(value->b32 == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,87,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->b32)-UINT64_C(0),32); + } + } + { /* b64 */ + if(!(value->b64 == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,88,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->b64)-UINT64_C(0),64); + } + } + { /* b12 */ + if(!(value->b12 == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,89,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->b12)-UINT64_C(0),12); + } + } + { /* b48 */ + if(!(value->b48 == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,90,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->b48)-UINT64_C(0),48); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t ranged_widths_measure_messages(const RangedWidths * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x08a60c07b54d8dc7): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->b8=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xff95701912ad97be): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>65535ull){decoded=65535ull; + r->report->clamped++; + } + value->b16=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xff9c5c1912b39470): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->b32=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xff92701912ab61e7): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->b64=(uint64_t)decoded; + } + break; + } + case UINT64_C(0xff95741912ad9e8a): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4095ull){decoded=4095ull; + r->report->clamped++; + } + value->b12=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xff8b681912a535a1): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>281474976710655ull){decoded=281474976710655ull; + r->report->clamped++; + } + value->b48=(uint64_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int ranged_widths_load_messages(RangedWidths * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_signed_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_signed_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_signed_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_signed_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define ranged_signed_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_unsigned_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_unsigned_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_unsigned_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_unsigned_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define ranged_unsigned_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_widths_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_widths_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_widths_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define ranged_widths_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define ranged_widths_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* ranged_signed_open: match the header and POINT. On a match the bytes ARE what this @@ -1840,6 +6000,8 @@ static SCHEMA_UNUSED int ranged_widths_load( RangedWidths * value, const uint8_t file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RangedSigned * ranged_signed_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RangedSigned *)table_cook_open_ex(bytes,length,sizeof(RangedSigned),SCHEMA_TABLE_ALIGNOF(RangedSigned),reason); } static SCHEMA_UNUSED const RangedSigned * ranged_signed_open( const void * bytes, uint64_t length ) { return (const RangedSigned *) table_cook_open( bytes, length, (uint64_t) sizeof( RangedSigned ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RangedSigned ) ); @@ -1864,6 +6026,8 @@ static SCHEMA_UNUSED const RangedSigned * ranged_signed_open( const void * bytes file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RangedUnsigned * ranged_unsigned_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RangedUnsigned *)table_cook_open_ex(bytes,length,sizeof(RangedUnsigned),SCHEMA_TABLE_ALIGNOF(RangedUnsigned),reason); } static SCHEMA_UNUSED const RangedUnsigned * ranged_unsigned_open( const void * bytes, uint64_t length ) { return (const RangedUnsigned *) table_cook_open( bytes, length, (uint64_t) sizeof( RangedUnsigned ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RangedUnsigned ) ); @@ -1888,11 +6052,107 @@ static SCHEMA_UNUSED const RangedUnsigned * ranged_unsigned_open( const void * b file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RangedWidths * ranged_widths_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RangedWidths *)table_cook_open_ex(bytes,length,sizeof(RangedWidths),SCHEMA_TABLE_ALIGNOF(RangedWidths),reason); } static SCHEMA_UNUSED const RangedWidths * ranged_widths_open( const void * bytes, uint64_t length ) { return (const RangedWidths *) table_cook_open( bytes, length, (uint64_t) sizeof( RangedWidths ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RangedWidths ) ); } +static SCHEMA_UNUSED int schema_tabledemo_ranged_signed_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RangedSigned * value=(const RangedSigned *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->i8_span,1,order); + table_cook_put(at+1,(uint64_t)value->i8_low,1,order); + table_cook_put(at+2,(uint64_t)value->i8_high,1,order); + table_cook_put(at+3,(uint64_t)value->i8_inside,1,order); + table_cook_put(at+4,(uint64_t)value->i16_span,2,order); + table_cook_put(at+6,(uint64_t)value->i16_low,2,order); + table_cook_put(at+8,(uint64_t)value->i16_high,2,order); + table_cook_put(at+10,(uint64_t)value->i16_inside,2,order); + table_cook_put(at+12,(uint64_t)value->i32_span,4,order); + table_cook_put(at+16,(uint64_t)value->i32_low,4,order); + table_cook_put(at+20,(uint64_t)value->i32_high,4,order); + table_cook_put(at+24,(uint64_t)value->i32_inside,4,order); + table_cook_put(at+32,(uint64_t)value->i64_span,8,order); + table_cook_put(at+40,(uint64_t)value->i64_low,8,order); + table_cook_put(at+48,(uint64_t)value->i64_high,8,order); + table_cook_put(at+56,(uint64_t)value->i64_inside,8,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<4;cook_i1++) { + table_cook_put(at+64+cook_i1*2,(uint64_t)value->edges[cook_i1],2,order); + } } + table_cook_put(at+64+8,(uint32_t)value->edges_count,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t ranged_signed_cook_measure(const RangedSigned * value) { (void)value; return 160; } +static SCHEMA_UNUSED int ranged_signed_cook(const RangedSigned * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<160) { return 0; } + memset(raw,0,160); + if(!schema_tabledemo_ranged_signed_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+152,UINT64_C(0xec79751dda28be2e),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,80,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_ranged_unsigned_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RangedUnsigned * value=(const RangedUnsigned *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->u8_span,1,order); + table_cook_put(at+1,(uint64_t)value->u8_low,1,order); + table_cook_put(at+2,(uint64_t)value->u8_high,1,order); + table_cook_put(at+3,(uint64_t)value->u8_inside,1,order); + table_cook_put(at+4,(uint64_t)value->u16_span,2,order); + table_cook_put(at+6,(uint64_t)value->u16_low,2,order); + table_cook_put(at+8,(uint64_t)value->u16_high,2,order); + table_cook_put(at+10,(uint64_t)value->u16_inside,2,order); + table_cook_put(at+12,(uint64_t)value->u32_span,4,order); + table_cook_put(at+16,(uint64_t)value->u32_low,4,order); + table_cook_put(at+20,(uint64_t)value->u32_high,4,order); + table_cook_put(at+24,(uint64_t)value->u32_inside,4,order); + table_cook_put(at+32,(uint64_t)value->u64_span,8,order); + table_cook_put(at+40,(uint64_t)value->u64_low,8,order); + table_cook_put(at+48,(uint64_t)value->u64_high,8,order); + table_cook_put(at+56,(uint64_t)value->u64_inside,8,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<4;cook_i1++) { + table_cook_put(at+64+cook_i1*8,(uint64_t)value->counts[cook_i1],8,order); + } } + table_cook_put(at+64+32,(uint32_t)value->counts_count,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t ranged_unsigned_cook_measure(const RangedUnsigned * value) { (void)value; return 184; } +static SCHEMA_UNUSED int ranged_unsigned_cook(const RangedUnsigned * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<184) { return 0; } + memset(raw,0,184); + if(!schema_tabledemo_ranged_unsigned_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+176,UINT64_C(0x031ec0c4b7d28bf1),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,104,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_ranged_widths_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RangedWidths * value=(const RangedWidths *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->b8,4,order); + table_cook_put(at+4,(uint64_t)value->b16,4,order); + table_cook_put(at+8,(uint64_t)value->b32,4,order); + table_cook_put(at+16,(uint64_t)value->b64,8,order); + table_cook_put(at+24,(uint64_t)value->b12,4,order); + table_cook_put(at+32,(uint64_t)value->b48,8,order); + return 1; +} +static SCHEMA_UNUSED int64_t ranged_widths_cook_measure(const RangedWidths * value) { (void)value; return 120; } +static SCHEMA_UNUSED int ranged_widths_cook(const RangedWidths * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<120) { return 0; } + memset(raw,0,120); + if(!schema_tabledemo_ranged_widths_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+112,UINT64_C(0xbf59694e9e4f5ed7),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,40,1,8,order); return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/examples-c/TabledemoView.c b/testdata/golden/tables/examples-c/TabledemoView.c new file mode 100644 index 000000000..f44a97370 --- /dev/null +++ b/testdata/golden/tables/examples-c/TabledemoView.c @@ -0,0 +1,112 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "TabledemoView.h" +const UnitViewInfo * schema_tabledemo_unit_view_(void) +{ + static const ViewVariant Difficulty_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Easy", 0x483d9e6c1ccd1f9bull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Normal", 0x9ff3121d30f88d52ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Hard", 0x58c7b5d87587287cull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; +static const char * const Grade_view_variant2_tags[] = { "celebrate", "rare", "announce" }; +static const char * const Grade_view_tags[] = { "progression" }; + static const ViewVariant Grade_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Bronze", 0xc4927739aac4c591ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Silver", 0xc3e51adeeaf12580ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Gold", 0xc416c97e0d3218b3ull, NULL, NULL, NULL, "The top rank. A profile that reaches it keeps it.", 3, Grade_view_variant2_tags }, + }; + static const ViewVariant Hull_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Interceptor", 0xae61a6cbe88c2cf4ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Gunship", 0x334d24e1420dbc1full, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Freighter", 0x6c2321a3d00e23dbull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant ShipType_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Fighter", 0xd011f7c3c15285c2ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Bomber", 0xa8216aa1c554cb8aull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Scout", 0x7d573c625719c1a5ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Team_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Red", 0x9ff1de19feac1b7cull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Blue", 0xecf3d3a7c1693e2dull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Green", 0xcf00d78fd5953f1cull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Weapon_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Cannon", 0x5854debe3b2e767cull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Missile", 0xb528592e5a4583e3ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 3, "Mine", 0x04dc16aea8ff5276ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVariant Perks_view_variants[] = { + { 0, "Shielded", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Cloaked", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "Turbo", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; +static const char * const Effect_view_variant0_tags[] = { "positive" }; +static const char * const Effect_view_tags[] = { "gameplay" }; + static const ViewVariant Effect_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "buff", 0xffb5be9be2e469ccull, "Buff", &schema_tabledemo_buff_info_, NULL, "A multiplier that rides while the effect is live.", 1, Effect_view_variant0_tags }, + { 2, "debuff", 0x13cdc5ede73d2fd7ull, "Debuff", &schema_tabledemo_debuff_info_, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "Difficulty", "Pack.schema", 3, 8, 4, Difficulty_view_variants, TableDocNone, 0, NULL }, + { "Grade", "Tables.schema", 3, 8, 4, Grade_view_variants, "A profile's standing, ranked low to high.", 1, Grade_view_tags }, + { "Hull", "Keyed.schema", 3, 8, 4, Hull_view_variants, TableDocNone, 0, NULL }, + { "ShipType", "Pack.schema", 3, 8, 4, ShipType_view_variants, TableDocNone, 0, NULL }, + { "Team", "Keyed.schema", 3, 8, 4, Team_view_variants, TableDocNone, 0, NULL }, + { "Weapon", "Keyed.schema", 3, 8, 4, Weapon_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary flags_decls[] = { + { "Perks", "Tables.schema", 2, 8, 3, Perks_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary unions[] = { + { "Effect", "Tables.schema", 2, 8, 3, Effect_view_variants, "What a weapon does to a target beyond its damage.", 1, Effect_view_tags }, + }; + static const ViewType types[] = { + { "Attachment", "Tables.schema", 0, &schema_tabledemo_attachment_info_, TableDocNone, 0, NULL }, + { "Buff", "Tables.schema", 0, &schema_tabledemo_buff_info_, TableDocNone, 0, NULL }, + { "Debuff", "Tables.schema", 0, &schema_tabledemo_debuff_info_, TableDocNone, 0, NULL }, + { "ScoreBoard", "Keyed.schema", 0, &schema_tabledemo_score_board_info_, TableDocNone, 0, NULL }, + }; +static const char * const WeaponConfig_view_tags[] = { "designer_facing" }; + static const ViewType tables[] = { + { "ArchiveConfig", "Nested.schema", 1, &schema_tabledemo_archive_config_info_, TableDocNone, 0, NULL }, + { "GlobalSettings", "Pack.schema", 1, &schema_tabledemo_global_settings_info_, TableDocNone, 0, NULL }, + { "GunnerConfig", "Keyed.schema", 1, &schema_tabledemo_gunner_config_info_, TableDocNone, 0, NULL }, + { "GunnerSettings", "Pack.schema", 1, &schema_tabledemo_gunner_settings_info_, TableDocNone, 0, NULL }, + { "HullConfig", "Keyed.schema", 1, &schema_tabledemo_hull_config_info_, TableDocNone, 0, NULL }, + { "KeyedConfig", "Keyed.schema", 1, &schema_tabledemo_keyed_config_info_, TableDocNone, 0, NULL }, + { "LoadoutConfig", "Tables.schema", 1, &schema_tabledemo_loadout_config_info_, TableDocNone, 0, NULL }, + { "PackConfig", "Pack.schema", 1, &schema_tabledemo_pack_config_info_, TableDocNone, 0, NULL }, + { "Patrol", "Guarded.schema", 1, &schema_tabledemo_patrol_info_, TableDocNone, 0, NULL }, + { "ProfileConfig", "Tables.schema", 1, &schema_tabledemo_profile_config_info_, TableDocNone, 0, NULL }, + { "RangedSigned", "Ranges.schema", 1, &schema_tabledemo_ranged_signed_info_, TableDocNone, 0, NULL }, + { "RangedUnsigned", "Ranges.schema", 1, &schema_tabledemo_ranged_unsigned_info_, TableDocNone, 0, NULL }, + { "RangedWidths", "Ranges.schema", 1, &schema_tabledemo_ranged_widths_info_, TableDocNone, 0, NULL }, + { "RootConfig", "Tables.schema", 1, &schema_tabledemo_root_config_info_, TableDocNone, 0, NULL }, + { "ShipEntry", "Pack.schema", 1, &schema_tabledemo_ship_entry_info_, TableDocNone, 0, NULL }, + { "TeamConfig", "Keyed.schema", 1, &schema_tabledemo_team_config_info_, TableDocNone, 0, NULL }, + { "TurretConfig", "Keyed.schema", 1, &schema_tabledemo_turret_config_info_, TableDocNone, 0, NULL }, + { "WeaponConfig", "Tables.schema", 1, &schema_tabledemo_weapon_config_info_, "One weapon's tuning. A designer edits this table and nothing else.", 1, WeaponConfig_view_tags }, + { "WideBlob", "Wide.schema", 1, &schema_tabledemo_wide_blob_info_, TableDocNone, 0, NULL }, + }; +static const char * const MaxLoadoutSlots_view_tags[] = { "tuning" }; + static const ViewConstant constants[] = { + { "MaxBlobBytes", "Wide.schema", "int64", 0, INT64_C(70000), 0.0, TableDocNone, 0, NULL }, + { "MaxLoadoutSlots", "Tables.schema", "int64", 0, INT64_C(8), 0.0, "The slots a loadout admits. Raising it is a free edit on the wire: a\nbound sizes storage, and it is not an id.", 1, MaxLoadoutSlots_view_tags }, + { "MaxProfileName", "Tables.schema", "int64", 0, INT64_C(32), 0.0, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "tabledemo", 0xba7b344f28584d33ull, + 4, types, + 19, tables, + 6, enums, + 1, flags_decls, + 1, unions, + 3, constants }; + return &info; +} diff --git a/testdata/golden/tables/examples-c/TabledemoView.h b/testdata/golden/tables/examples-c/TabledemoView.h new file mode 100644 index 000000000..5e3e74c89 --- /dev/null +++ b/testdata/golden/tables/examples-c/TabledemoView.h @@ -0,0 +1,115 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_TABLEDEMO_VIEW_H +#define SCHEMA_TABLEDEMO_VIEW_H +#include +#include +#include +#include "Guarded.h" +#include "Keyed.h" +#include "Nested.h" +#include "Pack.h" +#include "Ranges.h" +#include "Tables.h" +#include "Wide.h" +#include "GuardedTable.h" +#include "KeyedTable.h" +#include "NestedTable.h" +#include "PackTable.h" +#include "RangesTable.h" +#include "TablesTable.h" +#include "WideTable.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + + +const UnitViewInfo * schema_tabledemo_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_tabledemo_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/testdata/golden/tables/examples-c/TablesTable.c b/testdata/golden/tables/examples-c/TablesTable.c index 0a6a7cc54..b200e97ca 100644 --- a/testdata/golden/tables/examples-c/TablesTable.c +++ b/testdata/golden/tables/examples-c/TablesTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else - { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } - } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1730,45 +2331,45 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy static const TableVariantInfo schema_tabledemo_weapon_config_effect_variants_[] = { { "None", 0 }, - { "buff", 0xeae6 }, - { "debuff", 0xb0d3 }, + { "buff", 0xffb5be9be2e469ccull }, + { "debuff", 0x13cdc5ede73d2fd7ull }, }; static const TableUnionArmInfo schema_tabledemo_weapon_config_effect_arms_[] = { - { 0, NULL }, - { (uint32_t) offsetof( Effect, as.buff ), &schema_tabledemo_buff_info_ }, - { (uint32_t) offsetof( Effect, as.debuff ), &schema_tabledemo_debuff_info_ }, + { 0, NULL, NULL, 0 }, + { (uint32_t)offsetof( Effect, as.buff ), &schema_tabledemo_buff_info_, NULL, (uint32_t)sizeof( ((Effect *)0)->as.buff ) }, + { (uint32_t)offsetof( Effect, as.debuff ), &schema_tabledemo_debuff_info_, NULL, (uint32_t)sizeof( ((Effect *)0)->as.debuff ) }, }; static const TableUnionInfo schema_tabledemo_weapon_config_effect_union_ = { (uint32_t) offsetof( Effect, type ), (uint32_t) sizeof( ( (Effect *) 0 )->type ), schema_tabledemo_weapon_config_effect_arms_ }; static const char * const schema_tabledemo_weapon_config_damage_tags_[] = { "ui_slider", "hot", "tuning" }; static const char * const schema_tabledemo_weapon_config_tags_[] = { "designer_facing" }; static const TableFieldInfo schema_tabledemo_weapon_config_fields_[] = { - { "damage", "damage", "float32", 0x15a9, 10, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, damage ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", " Damage per hit, before armor.\n\nThe armor curve is written \"hardness \\ hit\" in the design notes,\nand the backslash is part of the name.", 3, schema_tabledemo_weapon_config_damage_tags_ }, - { "speed", "speed", "float32", 0x2e46, 10, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, speed ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->speed ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "penetration", "penetration", "int32", 0x6557, 4, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, penetration ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->penetration ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "channel", "channel", "bits(6)", 0x7366, 6, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, channel ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->channel ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 63.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "homing", "homing", "bool", 0xab40, 1, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, homing ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->homing ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "effect", "effect", "Effect", 0xe33a, 15, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, effect ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->effect ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 2, schema_tabledemo_weapon_config_effect_variants_, 1, NULL, NULL, -1, &schema_tabledemo_weapon_config_effect_union_, "", TableDocNone, 0, NULL }, + { "damage", "damage", "float32", 0x7f6308be8ab37fc0ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, damage ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->damage ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", " Damage per hit, before armor.\n\nThe armor curve is written \"hardness \\ hit\" in the design notes,\nand the backslash is part of the name.", 3, schema_tabledemo_weapon_config_damage_tags_, 0, NULL }, + { "speed", "speed", "float32", 0x1733055702acb1d2ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, speed ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->speed ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "penetration", "penetration", "int32", 0x4f31c5309e13e932ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, penetration ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->penetration ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "channel", "channel", "bits(6)", 0xa5013e9ad5caeda4ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, channel ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->channel ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 63.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "homing", "homing", "bool", 0xad6587bc602e3f59ull, 1, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, homing ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->homing ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "effect", "effect", "Effect", 0x36c1c83b51f24394ull, 15, 0, 0, 0, 0, (uint32_t) offsetof( WeaponConfig, effect ), (uint32_t) sizeof( ( (WeaponConfig *) 0 )->effect ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_tabledemo_weapon_config_effect_variants_, 1, NULL, NULL, -1, &schema_tabledemo_weapon_config_effect_union_, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_weapon_config_info_ = { "WeaponConfig", (uint32_t) sizeof( WeaponConfig ), 6, schema_tabledemo_weapon_config_fields_, schema_tabledemo_weapon_config_reset_raw_, "One weapon's tuning. A designer edits this table and nothing else.", 1, schema_tabledemo_weapon_config_tags_ }; static const TableVariantInfo schema_tabledemo_loadout_config_grade_variants_[] = { - { "None", 0x0000 }, - { "Bronze", 0x3407 }, - { "Silver", 0xa3e7 }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Bronze", 0xc4927739aac4c591ull }, + { "Silver", 0xc3e51adeeaf12580ull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableVariantInfo schema_tabledemo_loadout_config_grades_variants_[] = { - { "None", 0x0000 }, - { "Bronze", 0x3407 }, - { "Silver", 0xa3e7 }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Bronze", 0xc4927739aac4c591ull }, + { "Silver", 0xc3e51adeeaf12580ull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableVariantInfo schema_tabledemo_loadout_config_podium_variants_[] = { - { "None", 0x0000 }, - { "Bronze", 0x3407 }, - { "Silver", 0xa3e7 }, - { "Gold", 0xda27 }, + { "None", 0x0000000000000000ull }, + { "Bronze", 0xc4927739aac4c591ull }, + { "Silver", 0xc3e51adeeaf12580ull }, + { "Gold", 0xc416c97e0d3218b3ull }, }; static const TableVariantInfo schema_tabledemo_loadout_config_perks_variants_[] = { { "Shielded", 0 }, @@ -1776,58 +2377,58 @@ static const TableVariantInfo schema_tabledemo_loadout_config_perks_variants_[] { "Turbo", 0 }, }; static const TableFieldInfo schema_tabledemo_loadout_config_fields_[] = { - { "grade", "grade", "Grade", 0xd272, 7, 0, 0, 0, 0, (uint32_t) offsetof( LoadoutConfig, grade ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->grade ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_tabledemo_loadout_config_grade_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "grades", "grades", "Grade", 0x301a, 7, 1, 1, 0, 4, (uint32_t) offsetof( LoadoutConfig, grades ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->grades[0] ), (uint32_t) offsetof( LoadoutConfig, grades_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_tabledemo_loadout_config_grades_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "podium", "podium", "Grade", 0x088a, 7, 1, 0, 0, 3, (uint32_t) offsetof( LoadoutConfig, podium ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->podium[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 3, schema_tabledemo_loadout_config_podium_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "perks", "perks", "Perks", 0x2fc5, 9, 0, 0, 0, 0, (uint32_t) offsetof( LoadoutConfig, perks ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->perks ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, 2, schema_tabledemo_loadout_config_perks_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "primary", "primary", "WeaponConfig", 0x05f7, 13, 0, 0, 0, 0, (uint32_t) offsetof( LoadoutConfig, primary ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->primary ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_weapon_config_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "backups", "backups", "WeaponConfig", 0x1647, 13, 1, 0, 0, 2, (uint32_t) offsetof( LoadoutConfig, backups ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->backups[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_weapon_config_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "attachments", "attachments", "Attachment", 0x44d5, 13, 1, 1, 0, 8, (uint32_t) offsetof( LoadoutConfig, attachments ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->attachments[0] ), (uint32_t) offsetof( LoadoutConfig, attachments_count ), 0xffffffffu, &schema_tabledemo_attachment_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "grade", "grade", "Grade", 0x32a89b2977c48ad4ull, 30, 0, 0, 0, 0, (uint32_t) offsetof( LoadoutConfig, grade ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->grade ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_tabledemo_loadout_config_grade_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "grades", "grades", "Grade", 0xd90a4e7682f799c5ull, 30, 1, 1, 0, 4, (uint32_t) offsetof( LoadoutConfig, grades ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->grades[0] ), (uint32_t) offsetof( LoadoutConfig, grades_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_tabledemo_loadout_config_grades_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "podium", "podium", "Grade", 0xb46ff45a23d72549ull, 30, 1, 0, 0, 3, (uint32_t) offsetof( LoadoutConfig, podium ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->podium[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 3, schema_tabledemo_loadout_config_podium_variants_, 1, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "perks", "perks", "Perks", 0x4b06f5847096e54aull, 9, 0, 0, 0, 0, (uint32_t) offsetof( LoadoutConfig, perks ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->perks ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, 2, schema_tabledemo_loadout_config_perks_variants_, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "primary", "primary", "WeaponConfig", 0xbf31137ff783e939ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( LoadoutConfig, primary ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->primary ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_weapon_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "backups", "backups", "WeaponConfig", 0xde28f0f5118acc24ull, 13, 1, 0, 0, 2, (uint32_t) offsetof( LoadoutConfig, backups ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->backups[0] ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_weapon_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "attachments", "attachments", "Attachment", 0xf901aa0340249a41ull, 13, 1, 1, 0, 8, (uint32_t) offsetof( LoadoutConfig, attachments ), (uint32_t) sizeof( ( (LoadoutConfig *) 0 )->attachments[0] ), (uint32_t) offsetof( LoadoutConfig, attachments_count ), 0xffffffffu, &schema_tabledemo_attachment_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_loadout_config_info_ = { "LoadoutConfig", (uint32_t) sizeof( LoadoutConfig ), 7, schema_tabledemo_loadout_config_fields_, schema_tabledemo_loadout_config_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_profile_config_fields_[] = { - { "name", "name", "string", 0x30df, 12, 0, 1, 0, 32, (uint32_t) offsetof( ProfileConfig, name ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->name ), (uint32_t) offsetof( ProfileConfig, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "icon", "icon", "bytes", 0xf3b0, 6, 1, 1, 0, 16, (uint32_t) offsetof( ProfileConfig, icon ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->icon[0] ), (uint32_t) offsetof( ProfileConfig, icon_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "experience", "experience", "uint32", 0x61d8, 8, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, experience ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->experience ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "tilt", "tilt", "int8", 0x023c, 2, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, tilt ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->tilt ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "heading", "heading", "int16", 0x885d, 3, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, heading ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->heading ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "timestamp", "timestamp", "int64", 0x67a0, 5, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, timestamp ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->timestamp ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "badge", "badge", "uint8", 0xb71c, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, badge ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->badge ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "port", "port", "uint16", 0x6942, 7, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, port ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->port ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "epoch", "epoch", "uint64", 0xf4bd, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, epoch ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->epoch ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "precision", "precision", "float64", 0x96e0, 11, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, precision ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->precision ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "ratings", "ratings", "float32", 0x97dd, 10, 1, 0, 0, 4, (uint32_t) offsetof( ProfileConfig, ratings ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->ratings[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "has_loadout", "has_loadout", "bool", 0xb4db, 1, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, has_loadout ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->has_loadout ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "loadout", "loadout", "LoadoutConfig", 0x9f78, 13, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, loadout ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->loadout ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_loadout_config_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "has_loadout", TableDocNone, 0, NULL }, + { "name", "name", "string", 0xc4bcadba8e631b86ull, 12, 0, 1, 0, 32, (uint32_t) offsetof( ProfileConfig, name ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->name ), (uint32_t) offsetof( ProfileConfig, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "icon", "icon", "bytes", 0xdbff4cc56c2092f0ull, 6, 1, 1, 0, 16, (uint32_t) offsetof( ProfileConfig, icon ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->icon[0] ), (uint32_t) offsetof( ProfileConfig, icon_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "experience", "experience", "uint32", 0xab8b6d521f80b969ull, 8, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, experience ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->experience ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tilt", "tilt", "int8", 0x1e5088ef2e9bafc6ull, 2, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, tilt ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->tilt ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "heading", "heading", "int16", 0xb128829190ca0f75ull, 3, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, heading ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->heading ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "timestamp", "timestamp", "int64", 0x5ddc92338ef53403ull, 5, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, timestamp ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->timestamp ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "badge", "badge", "uint8", 0x10bda981fe095518ull, 6, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, badge ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->badge ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "port", "port", "uint16", 0x8c2cdb0da8933fa6ull, 7, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, port ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->port ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "epoch", "epoch", "uint64", 0xfc9697d1196e6ba6ull, 9, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, epoch ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->epoch ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "precision", "precision", "float64", 0x288427f5babd05f7ull, 11, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, precision ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->precision ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ratings", "ratings", "float32", 0xb921eb8a3d0cb0f9ull, 10, 1, 0, 0, 4, (uint32_t) offsetof( ProfileConfig, ratings ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->ratings[0] ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "has_loadout", "has_loadout", "bool", 0xa06f5e0a148e253cull, 1, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, has_loadout ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->has_loadout ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "loadout", "loadout", "LoadoutConfig", 0x5759ce7586bbb5a3ull, 13, 0, 0, 0, 0, (uint32_t) offsetof( ProfileConfig, loadout ), (uint32_t) sizeof( ( (ProfileConfig *) 0 )->loadout ), 0xffffffffu, 0xffffffffu, &schema_tabledemo_loadout_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "has_loadout", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_profile_config_info_ = { "ProfileConfig", (uint32_t) sizeof( ProfileConfig ), 13, schema_tabledemo_profile_config_fields_, schema_tabledemo_profile_config_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_root_config_fields_[] = { - { "version_note", "version_note", "string", 0xe726, 12, 0, 1, 0, 16, (uint32_t) offsetof( RootConfig, version_note ), (uint32_t) sizeof( ( (RootConfig *) 0 )->version_note ), (uint32_t) offsetof( RootConfig, version_note_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "weapons", "weapons", "WeaponConfig", 0x91cd, 13, 1, 1, 0, 8, (uint32_t) offsetof( RootConfig, weapons ), (uint32_t) sizeof( ( (RootConfig *) 0 )->weapons[0] ), (uint32_t) offsetof( RootConfig, weapons_count ), 0xffffffffu, &schema_tabledemo_weapon_config_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "profiles", "profiles", "ProfileConfig", 0x73fd, 13, 1, 1, 0, 4, (uint32_t) offsetof( RootConfig, profiles ), (uint32_t) sizeof( ( (RootConfig *) 0 )->profiles[0] ), (uint32_t) offsetof( RootConfig, profiles_count ), 0xffffffffu, &schema_tabledemo_profile_config_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "version_note", "version_note", "string", 0x8a67c9728746d394ull, 12, 0, 1, 0, 16, (uint32_t) offsetof( RootConfig, version_note ), (uint32_t) sizeof( ( (RootConfig *) 0 )->version_note ), (uint32_t) offsetof( RootConfig, version_note_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "weapons", "weapons", "WeaponConfig", 0x41cbd901b87fabb6ull, 13, 1, 1, 0, 8, (uint32_t) offsetof( RootConfig, weapons ), (uint32_t) sizeof( ( (RootConfig *) 0 )->weapons[0] ), (uint32_t) offsetof( RootConfig, weapons_count ), 0xffffffffu, &schema_tabledemo_weapon_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "profiles", "profiles", "ProfileConfig", 0x8181e61fc0436767ull, 13, 1, 1, 0, 4, (uint32_t) offsetof( RootConfig, profiles ), (uint32_t) sizeof( ( (RootConfig *) 0 )->profiles[0] ), (uint32_t) offsetof( RootConfig, profiles_count ), 0xffffffffu, &schema_tabledemo_profile_config_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_root_config_info_ = { "RootConfig", (uint32_t) sizeof( RootConfig ), 3, schema_tabledemo_root_config_fields_, schema_tabledemo_root_config_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_attachment_fields_[] = { - { "slot", "slot", "int32", 0x37e4, 4, 0, 0, 0, 0, (uint32_t) offsetof( Attachment, slot ), (uint32_t) sizeof( ( (Attachment *) 0 )->slot ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "power", "power", "float32", 0xd609, 10, 0, 0, 0, 0, (uint32_t) offsetof( Attachment, power ), (uint32_t) sizeof( ( (Attachment *) 0 )->power ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "slot", "slot", "int32", 0x6a771618f6fe31d1ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( Attachment, slot ), (uint32_t) sizeof( ( (Attachment *) 0 )->slot ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 7.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "power", "power", "float32", 0xeef9d1358ae7b4e6ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Attachment, power ), (uint32_t) sizeof( ( (Attachment *) 0 )->power ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_attachment_info_ = { "Attachment", (uint32_t) sizeof( Attachment ), 2, schema_tabledemo_attachment_fields_, schema_tabledemo_attachment_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_buff_fields_[] = { - { "multiplier", "multiplier", "float32", 0x32e0, 10, 0, 0, 0, 0, (uint32_t) offsetof( Buff, multiplier ), (uint32_t) sizeof( ( (Buff *) 0 )->multiplier ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "multiplier", "multiplier", "float32", 0x9adc623a805c87c6ull, 10, 0, 0, 0, 0, (uint32_t) offsetof( Buff, multiplier ), (uint32_t) sizeof( ( (Buff *) 0 )->multiplier ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_buff_info_ = { "Buff", (uint32_t) sizeof( Buff ), 1, schema_tabledemo_buff_fields_, schema_tabledemo_buff_reset_raw_, TableDocNone, 0, NULL }; static const TableFieldInfo schema_tabledemo_debuff_fields_[] = { - { "amount", "amount", "int32", 0x39cc, 4, 0, 0, 0, 0, (uint32_t) offsetof( Debuff, amount ), (uint32_t) sizeof( ( (Debuff *) 0 )->amount ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "amount", "amount", "int32", 0x8113fe7ea2b16969ull, 4, 0, 0, 0, 0, (uint32_t) offsetof( Debuff, amount ), (uint32_t) sizeof( ( (Debuff *) 0 )->amount ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 100.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_debuff_info_ = { "Debuff", (uint32_t) sizeof( Debuff ), 1, schema_tabledemo_debuff_fields_, schema_tabledemo_debuff_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/examples-c/TablesTable.h b/testdata/golden/tables/examples-c/TablesTable.h index 56988f580..e6206d323 100644 --- a/testdata/golden/tables/examples-c/TablesTable.h +++ b/testdata/golden/tables/examples-c/TablesTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package tabledemo — protocol id 0xba7b344f28584d33 (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_TABLEDEMO_TABLESTABLE_H #define SCHEMA_TABLEDEMO_TABLESTABLE_H @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -183,6 +194,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -210,6 +223,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -231,157 +246,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[156]; + uint32_t slots[512], positions[156]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (512-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(512-1); + } + if(v->count==156) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } } - return 0; + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -391,6 +526,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_TABLEDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_TABLEDEMO_BUILD_VERSION #define SCHEMA_TABLEDEMO_BUILD_VERSION @@ -502,25 +692,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -530,40 +720,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_TABLEDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + // One weapon's tuning. A designer edits this table and nothing else. /* table WeaponConfig — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in weapon_config_reset and nowhere @@ -738,17 +1509,17 @@ static SCHEMA_UNUSED void schema_tabledemo_debuff_reset_raw_( void * storage ) { /* ---- codecs: measure/save/load per closure member ---- */ static SCHEMA_UNUSED int64_t weapon_config_measure( const WeaponConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_save_body( TableWriter * w, const WeaponConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_load_body( TableReader * r, WeaponConfig * value ); +static SCHEMA_UNUSED int weapon_config_save_body( TableWriter * w, const WeaponConfig * value ); +static SCHEMA_UNUSED int weapon_config_load_body( TableReader * r, WeaponConfig * value ); static SCHEMA_UNUSED int64_t loadout_config_measure( const LoadoutConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_save_body( TableWriter * w, const LoadoutConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_load_body( TableReader * r, LoadoutConfig * value ); +static SCHEMA_UNUSED int loadout_config_save_body( TableWriter * w, const LoadoutConfig * value ); +static SCHEMA_UNUSED int loadout_config_load_body( TableReader * r, LoadoutConfig * value ); static SCHEMA_UNUSED int64_t profile_config_measure( const ProfileConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_save_body( TableWriter * w, const ProfileConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( TableReader * r, ProfileConfig * value ); +static SCHEMA_UNUSED int profile_config_save_body( TableWriter * w, const ProfileConfig * value ); +static SCHEMA_UNUSED int profile_config_load_body( TableReader * r, ProfileConfig * value ); static SCHEMA_UNUSED int64_t root_config_measure( const RootConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int root_config_save_body( TableWriter * w, const RootConfig * value ); -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int root_config_load_body( TableReader * r, RootConfig * value ); +static SCHEMA_UNUSED int root_config_save_body( TableWriter * w, const RootConfig * value ); +static SCHEMA_UNUSED int root_config_load_body( TableReader * r, RootConfig * value ); static SCHEMA_UNUSED int64_t attachment_measure( const Attachment * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int attachment_save_body( TableWriter * w, const Attachment * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int attachment_load_body( TableReader * r, Attachment * value ); @@ -759,110 +1530,282 @@ static SCHEMA_UNUSED int64_t debuff_measure( const Debuff * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int debuff_save_body( TableWriter * w, const Debuff * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int debuff_load_body( TableReader * r, Debuff * value ); -static SCHEMA_UNUSED int64_t weapon_config_measure( const WeaponConfig * value ) +static SCHEMA_UNUSED int schema_tabledemo_weapon_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_loadout_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_profile_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_root_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_attachment_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_buff_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_tabledemo_debuff_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int weapon_config_save_message_body(TableBitWriter *,const WeaponConfig *); +static SCHEMA_UNUSED int loadout_config_save_message_body(TableBitWriter *,const LoadoutConfig *); +static SCHEMA_UNUSED int profile_config_save_message_body(TableBitWriter *,const ProfileConfig *); +static SCHEMA_UNUSED int root_config_save_message_body(TableBitWriter *,const RootConfig *); +static SCHEMA_UNUSED int attachment_save_message_body(TableBitWriter *,const Attachment *); +static SCHEMA_UNUSED int buff_save_message_body(TableBitWriter *,const Buff *); +static SCHEMA_UNUSED int debuff_save_message_body(TableBitWriter *,const Debuff *); +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_message_save_(TableBitWriter *,const Effect *); +static SCHEMA_UNUSED int weapon_config_load_message_body(TableMessageReader *,WeaponConfig *); +static SCHEMA_UNUSED int loadout_config_load_message_body(TableMessageReader *,LoadoutConfig *); +static SCHEMA_UNUSED int profile_config_load_message_body(TableMessageReader *,ProfileConfig *); +static SCHEMA_UNUSED int root_config_load_message_body(TableMessageReader *,RootConfig *); +static SCHEMA_UNUSED int attachment_load_message_body(TableMessageReader *,Attachment *); +static SCHEMA_UNUSED int buff_load_message_body(TableMessageReader *,Buff *); +static SCHEMA_UNUSED int debuff_load_message_body(TableMessageReader *,Debuff *); +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_message_load_(TableMessageReader *,Effect *); +static SCHEMA_UNUSED int schema_tabledemo_weapon_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_loadout_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_profile_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_root_config_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_attachment_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_buff_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_debuff_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_effect_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_message_save_(TableBitWriter * w,const Effect * value) +{ + switch(value->type) { + case EFFECT_TYPE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case EFFECT_TYPE_BUFF: { + table_bit_put(w,128,kTableMessageRefBitsHere); + if(!buff_save_message_body(w,&(*value).as.buff)) return 0; + break; + } + case EFFECT_TYPE_DEBUFF: { + table_bit_put(w,129,kTableMessageRefBitsHere); + if(!debuff_save_message_body(w,&(*value).as.debuff)) return 0; + break; + } + default: return 0; + } return !w->overflow; +} +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_message_load_(TableMessageReader * r,Effect * value) +{ + const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + value->type=0; + if(entry==NULL)return 1; + if(entry->kind==0)goto malformed; + switch(entry->id){ + case UINT64_C(0xffb5be9be2e469cc): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + memset(&(*value).as.buff,0,sizeof((*value).as.buff)); + if(!buff_load_message_body(r,&(*value).as.buff))return 0; + value->type=EFFECT_TYPE_BUFF; + break; + } + case UINT64_C(0x13cdc5ede73d2fd7): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + memset(&(*value).as.debuff,0,sizeof((*value).as.debuff)); + if(!debuff_load_message_body(r,&(*value).as.debuff))return 0; + value->type=EFFECT_TYPE_DEBUFF; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + }return 1; + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_tabledemo_effect_message_extent_(TableMessageReader * r,int64_t * at) +{ + const TableMessageEntry * entry; + (void)at; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(entry->kind==0)return 0; + switch(entry->id){ + default:break; + }return table_message_skip(r,entry); +} +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_save_( TableWriter * w, const Effect * value ); +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_load_( TableReader * r, Effect * value, int element ); +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_arm_buff_( TableWriter * w, const Effect * value ) +{ + if ( !buff_save_body( w, &(*value).as.buff ) ) { return 0; } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_arm_debuff_( TableWriter * w, const Effect * value ) +{ + if ( !debuff_save_body( w, &(*value).as.debuff ) ) { return 0; } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_save_( TableWriter * w, const Effect * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->damage != 21.0f ) { bytes += 3 + 4; } /* damage */ - if ( value->speed != 500.0f ) { bytes += 3 + 4; } /* speed */ - if ( value->penetration != 1 ) { bytes += 3 + 4; } /* penetration */ - if ( value->channel != 0 ) { bytes += 3 + 1; } /* channel */ - if ( value->homing != 0 ) { bytes += 3 + 1; } /* homing */ - switch ( value->effect.type ) /* effect */ + switch ( value->type ) { - case EFFECT_TYPE_NONE: break; /* None elides — TLV absence is the None */ + case EFFECT_TYPE_NONE: table_writer_leb( w, 0 ); break; case EFFECT_TYPE_BUFF: + table_writer_id( w, 0xffb5be9be2e469ccull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_effect_wire_arm_buff_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_effect_wire_arm_buff_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_effect_wire_arm_buff_( w, value ) ) { return 0; } + } + break; + case EFFECT_TYPE_DEBUFF: + table_writer_id( w, 0x13cdc5ede73d2fd7ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_effect_wire_arm_debuff_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_effect_wire_arm_debuff_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_effect_wire_arm_debuff_( w, value ) ) { return 0; } + } + break; + default: return 0; + } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_effect_wire_load_( TableReader * r, Effect * value, int element ) +{ + uint64_t ref; uint8_t kind; TableReader arm; + if ( !table_reader_leb( r, &ref ) || ref > r->id_count ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { value->type = 0; return 1; } + if ( element ) { value->type = 0; } + if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + kind = table_reader_get8( r ); + if ( !table_reader_span( r, &arm ) ) { r->report->malformed = 1; return 0; } + value->type = 0; + switch ( table_reader_id_at( r, ref ) ) + { + case 0xffb5be9be2e469ccull: { - int64_t arm_effect = buff_measure( &value->effect.as.buff ); - if ( arm_effect < 0 ) { return -1; } - bytes += 3 + 2 + 4 + arm_effect; /* the u16 ARM ID, then the arm length-prefixed */ + if ( kind != 13 && !table_kind_widens( kind, 13 ) ) { r->report->kind_mismatch++; break; } + buff_load_body( &arm, &(*value).as.buff ); + if ( arm.offset != arm.size ) { r->report->malformed = 1; break; } + if ( kind != 13 ) { r->report->widened++; } + value->type = EFFECT_TYPE_BUFF; break; } - case EFFECT_TYPE_DEBUFF: + case 0x13cdc5ede73d2fd7ull: { - int64_t arm_effect = debuff_measure( &value->effect.as.debuff ); - if ( arm_effect < 0 ) { return -1; } - bytes += 3 + 2 + 4 + arm_effect; /* the u16 ARM ID, then the arm length-prefixed */ + if ( kind != 13 && !table_kind_widens( kind, 13 ) ) { r->report->kind_mismatch++; break; } + debuff_load_body( &arm, &(*value).as.debuff ); + if ( arm.offset != arm.size ) { r->report->malformed = 1; break; } + if ( kind != 13 ) { r->report->widened++; } + value->type = EFFECT_TYPE_DEBUFF; break; } - default: return -1; /* invalid tag — the write side refuses it too */ + default: r->report->unknown++; break; } - return bytes; + return 1; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_save_body( TableWriter * w, const WeaponConfig * value ) +static SCHEMA_UNUSED int weapon_config_save_body( TableWriter * w, const WeaponConfig * value ) { - if ( value->damage != 21.0f ) - { - table_writer_put16( w, 0x15a9 ); table_writer_put8( w, 10 ); /* damage */ - table_writer_put32( w, table_float_to_bits( value->damage ) ); - } - if ( value->speed != 500.0f ) - { - table_writer_put16( w, 0x2e46 ); table_writer_put8( w, 10 ); /* speed */ - table_writer_put32( w, table_float_to_bits( value->speed ) ); + (void) value; + { /* damage */ + if ( !( value->damage == 21.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7f6308be8ab37fc0ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->damage ) ); + } } - if ( value->penetration != 1 ) - { - table_writer_put16( w, 0x6557 ); table_writer_put8( w, 4 ); /* penetration */ - table_writer_put32( w, (uint32_t) ( value->penetration ) ); + { /* speed */ + if ( !( value->speed == 500.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1733055702acb1d2ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->speed ) ); + } } - if ( value->channel != 0 ) - { - table_writer_put16( w, 0x7366 ); table_writer_put8( w, 6 ); /* channel */ - table_writer_put8( w, (uint8_t) ( value->channel ) ); + { /* penetration */ + if ( !( value->penetration == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4f31c5309e13e932ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->penetration ) ); + } } - if ( value->homing != 0 ) - { - table_writer_put16( w, 0xab40 ); table_writer_put8( w, 1 ); /* homing */ - table_writer_put8( w, value->homing ? 1 : 0 ); + { /* channel */ + if ( !( value->channel == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa5013e9ad5caeda4ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->channel ) ); + } } - if ( value->effect.type != EFFECT_TYPE_NONE ) - { - int64_t len_at_effect; - table_writer_put16( w, 0xe33a ); table_writer_put8( w, 15 ); /* effect */ - /* the ARM ID is the hash of the arm's NAME (docs/SPEC-TABLES.md §5), so - arms may be added anywhere, removed and reordered */ - switch ( value->effect.type ) + { /* homing */ + if ( !( value->homing == 0 ) ) { - case EFFECT_TYPE_BUFF: table_writer_put16( w, 0xeae6 ); break; - case EFFECT_TYPE_DEBUFF: table_writer_put16( w, 0xb0d3 ); break; - default: return 0; /* write validates the tag before it rides */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xad6587bc602e3f59ull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->homing ? 1 : 0 ); } - len_at_effect = w->offset; table_writer_put32( w, 0 ); - switch ( value->effect.type ) + } + { /* effect */ + if ( value->effect.type != EFFECT_TYPE_NONE ) { - case EFFECT_TYPE_BUFF: if ( !buff_save_body( w, &value->effect.as.buff ) ) { return 0; } break; - case EFFECT_TYPE_DEBUFF: if ( !debuff_save_body( w, &value->effect.as.debuff ) ) { return 0; } break; - default: return 0; /* write validates the tag before it rides */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x36c1c83b51f24394ull ); table_writer_put8( w, 15 ); + if ( !schema_tabledemo_effect_wire_save_( w, &value->effect ) ) { return 0; } } - table_writer_patch32( w, len_at_effect, (uint32_t) ( w->offset - len_at_effect - 4 ) ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t weapon_config_measure( const WeaponConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !weapon_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t weapon_config_save( const WeaponConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !weapon_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == weapon_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_load_body( TableReader * r, WeaponConfig * value ) +static SCHEMA_UNUSED int weapon_config_load_body( TableReader * r, WeaponConfig * value ) { - weapon_config_reset( value ); /* prefill declared defaults in place, then overlay */ + weapon_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x15a9: /* damage */ + case 0x7f6308be8ab37fc0ull: /* damage */ { if ( kind != 10 ) { @@ -870,11 +1813,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->damage = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->damage = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x2e46: /* speed */ + case 0x1733055702acb1d2ull: /* speed */ { if ( kind != 10 ) { @@ -882,28 +1833,100 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->speed = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->speed = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x6557: /* penetration */ + case 0x4f31c5309e13e932ull: /* penetration */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 10ll ) { decoded_wide = 10ll; r->report->clamped++; } + value->penetration = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 10ll ) { decoded_wide = 10ll; r->report->clamped++; } + value->penetration = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 10 ) { decoded_v = 10; r->report->clamped++; } + value->penetration = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 10 ) { decoded_v = 10; r->report->clamped++; } - value->penetration = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 10ll ) { decoded_wide = 10ll; r->report->clamped++; } + value->penetration = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 10ll ) { decoded_wide = 10ll; r->report->clamped++; } + value->penetration = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 10 ) { decoded_v = 10; r->report->clamped++; } + value->penetration = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x7366: /* channel */ + case 0xa5013e9ad5caeda4ull: /* channel */ { if ( kind != 6 ) { @@ -911,15 +1934,23 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - if ( decoded_v > 63ull ) { decoded_v = 63ull; r->report->clamped++; } /* bits(6) width clamp */ - value->channel = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + if ( decoded_v > 63ull ) { decoded_v = 63ull; r->report->clamped++; } /* bits(6) width clamp */ + value->channel = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xab40: /* homing */ + case 0xad6587bc602e3f59ull: /* homing */ { if ( kind != 1 ) { @@ -927,11 +1958,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->homing = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->homing = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0xe33a: /* effect */ + case 0x36c1c83b51f24394ull: /* effect */ { if ( kind != 15 ) { @@ -939,321 +1978,537 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int weapon_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint16_t arm_id; - uint32_t body_len; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - arm_id = table_reader_get16( r ); - if ( arm_id == 0 ) { value->effect.type = EFFECT_TYPE_NONE; break; } /* empty: the id is the whole payload */ - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - switch ( arm_id ) /* the arm's NAME hash (docs/SPEC-TABLES.md §5) */ - { - case 0xeae6: /* buff */ - value->effect.type = EFFECT_TYPE_BUFF; - buff_load_body( &sub, &value->effect.as.buff ); - break; - case 0xb0d3: /* debuff */ - value->effect.type = EFFECT_TYPE_DEBUFF; - debuff_load_body( &sub, &value->effect.as.debuff ); - break; - default: - /* an arm this reader cannot name: the value reads EMPTY and - the body is skipped by its length, never misdecoded. The - reset is explicit, not the prefill's: a repeated field id - must not leave an arm decoded by an earlier occurrence - standing (docs/SPEC-TABLES.md §4). */ - value->effect.type = EFFECT_TYPE_NONE; - r->report->unknown++; - break; - } - } - r->offset += body_len; + if ( !schema_tabledemo_effect_wire_load_( r, &value->effect, 0 ) ) { return 0; } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int weapon_config_load( WeaponConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + weapon_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return weapon_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t loadout_config_measure( const LoadoutConfig * value ) +static SCHEMA_UNUSED int weapon_config_save_message_body(TableBitWriter * w,const WeaponConfig * value) +{ + (void)value; + { /* damage */ + if(!(value->damage == 21.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,100,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->damage),32); + } + } + { /* speed */ + if(!(value->speed == 500.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,102,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->speed),32); + } + } + { /* penetration */ + if(!(value->penetration == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,103,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->penetration)-UINT64_C(0),4); + } + } + { /* channel */ + if(!(value->channel == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,104,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->channel)-UINT64_C(0),6); + } + } + { /* homing */ + if(!(value->homing == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,105,kTableMessageRefBitsHere); + table_bit_put(w,value->homing ? 1 : 0,1); + } + } + { /* effect */ + if(value->effect.type!=EFFECT_TYPE_NONE) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,106,kTableMessageRefBitsHere); + if(!schema_tabledemo_effect_wire_message_save_(w,&value->effect)) return 0; + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t weapon_config_measure_messages(const WeaponConfig * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x7f6308be8ab37fc0): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->damage=decoded; + } + break; + } + case UINT64_C(0x1733055702acb1d2): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->speed=decoded; + } + break; + } + case UINT64_C(0x4f31c5309e13e932): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>10ll){decoded=10ll; + r->report->clamped++; + } + value->penetration=(int32_t)decoded; + } + break; + } + case UINT64_C(0xa5013e9ad5caeda4): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>63ull){decoded=63ull; + r->report->clamped++; + } + value->channel=(uint32_t)decoded; + } + break; + } + case UINT64_C(0xad6587bc602e3f59): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->homing=lo!=0; + } + break; + } + case UINT64_C(0x36c1c83b51f24394): { + if(!(entry->kind==15 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,15)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!schema_tabledemo_effect_wire_message_load_(r,&value->effect))return 0; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int weapon_config_load_messages(WeaponConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_tabledemo_loadout_config_wire_grades_( TableWriter * w, const LoadoutConfig * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->grade != GRADE_SILVER ) + if ( value->grades_count < 0 || value->grades_count > 4 ) { return 0; } + int32_t i; + table_writer_put8( w, 30 ); table_writer_leb( w, (uint64_t) value->grades_count ); + for ( i = 0; i < value->grades_count; i++ ) { - uint16_t id_grade = 0; - switch ( value->grade ) /* Grade rides as the u16 hash of its VARIANT NAME (§5) */ + switch ( value->grades[i] ) { - case GRADE_NONE: id_grade = 0; break; - case GRADE_BRONZE: id_grade = 0x3407; break; - case GRADE_SILVER: id_grade = 0xa3e7; break; - case GRADE_GOLD: id_grade = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ + case GRADE_NONE: table_writer_leb( w, 0 ); break; + case GRADE_BRONZE: table_writer_id( w, 0xc4927739aac4c591ull ); break; + case GRADE_SILVER: table_writer_id( w, 0xc3e51adeeaf12580ull ); break; + case GRADE_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ } - (void) id_grade; - bytes += 3 + 2; /* grade: the variant's name hash */ } - if ( value->grades_count < 0 || value->grades_count > 4 ) { return -1; } /* storage invariant */ - if ( value->grades_count > 0 ) + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_loadout_config_wire_podium_( TableWriter * w, const LoadoutConfig * value ) +{ + int32_t i; + table_writer_put8( w, 30 ); table_writer_leb( w, (uint64_t) 3 ); + for ( i = 0; i < 3; i++ ) { + switch ( value->podium[i] ) { - int32_t element_i; - for ( element_i = 0; element_i < value->grades_count; element_i++ ) /* grades: every element must be nameable */ - { - uint16_t element_id = 0; - switch ( value->grades[element_i] ) /* Grade rides as the u16 hash of its VARIANT NAME (§5) */ - { - case GRADE_NONE: element_id = 0; break; - case GRADE_BRONZE: element_id = 0x3407; break; - case GRADE_SILVER: element_id = 0xa3e7; break; - case GRADE_GOLD: element_id = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) element_id; - } + case GRADE_NONE: table_writer_leb( w, 0 ); break; + case GRADE_BRONZE: table_writer_id( w, 0xc4927739aac4c591ull ); break; + case GRADE_SILVER: table_writer_id( w, 0xc3e51adeeaf12580ull ); break; + case GRADE_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ } - bytes += 3 + 4 + 5 + (int64_t) value->grades_count * 2; /* grades */ } + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_loadout_config_wire_backups_( TableWriter * w, const LoadoutConfig * value ) +{ + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) 2 ); + for ( i = 0; i < 2; i++ ) { - int all_default_podium = 1; - int32_t i; - for ( i = 0; i < 3; i++ ) { if ( value->podium[i] != GRADE_NONE ) { all_default_podium = 0; break; } } - if ( !all_default_podium ) + if ( w->buffer == NULL ) { - { - int32_t element_i; - for ( element_i = 0; element_i < 3; element_i++ ) /* podium: every element must be nameable */ - { - uint16_t element_id = 0; - switch ( value->podium[element_i] ) /* Grade rides as the u16 hash of its VARIANT NAME (§5) */ - { - case GRADE_NONE: element_id = 0; break; - case GRADE_BRONZE: element_id = 0x3407; break; - case GRADE_SILVER: element_id = 0xa3e7; break; - case GRADE_GOLD: element_id = 0xda27; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) element_id; - } - } - bytes += 3 + 4 + 5 + 6; /* podium */ + int64_t frame_begin = w->offset; + if ( !weapon_config_save_body( w, &value->backups[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - } - if ( value->perks != 0 ) { bytes += 3 + 8; } /* perks */ - { - int64_t body_primary = weapon_config_measure( &value->primary ); - if ( body_primary < 0 ) { return -1; } - if ( body_primary > 2 ) { bytes += 3 + 4 + body_primary; } /* primary: all-default nested elides */ - } - { - int32_t i; - bytes += 3 + 4 + 5; /* backups (fixed [2]) */ - for ( i = 0; i < 2; i++ ) + else { - int64_t elem_backups = weapon_config_measure( &value->backups[i] ); - if ( elem_backups < 0 ) { return -1; } - bytes += 4 + elem_backups; + TableWriter probe = table_writer_probe( w ); + if ( !weapon_config_save_body( &probe, &value->backups[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !weapon_config_save_body( w, &value->backups[i] ) ) { return 0; } } } - if ( value->attachments_count < 0 || value->attachments_count > 8 ) { return -1; } /* storage invariant */ - if ( value->attachments_count > 0 ) + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_loadout_config_wire_attachments_( TableWriter * w, const LoadoutConfig * value ) +{ + if ( value->attachments_count < 0 || value->attachments_count > 8 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->attachments_count ); + for ( i = 0; i < value->attachments_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* attachments */ - for ( i = 0; i < value->attachments_count; i++ ) + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !attachment_save_body( w, &value->attachments[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_attachments = attachment_measure( &value->attachments[i] ); - if ( elem_attachments < 0 ) { return -1; } - bytes += 4 + elem_attachments; + TableWriter probe = table_writer_probe( w ); + if ( !attachment_save_body( &probe, &value->attachments[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !attachment_save_body( w, &value->attachments[i] ) ) { return 0; } } } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_save_body( TableWriter * w, const LoadoutConfig * value ) +static SCHEMA_UNUSED int loadout_config_save_body( TableWriter * w, const LoadoutConfig * value ) { - if ( value->grade != GRADE_SILVER ) - { - uint16_t id_grade = 0; - switch ( value->grade ) /* Grade rides as the u16 hash of its VARIANT NAME (§5) */ + (void) value; + { /* grade */ + if ( !( value->grade == GRADE_SILVER ) ) { - case GRADE_NONE: id_grade = 0; break; - case GRADE_BRONZE: id_grade = 0x3407; break; - case GRADE_SILVER: id_grade = 0xa3e7; break; - case GRADE_GOLD: id_grade = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x32a89b2977c48ad4ull ); table_writer_put8( w, 30 ); + switch ( value->grade ) + { + case GRADE_NONE: table_writer_leb( w, 0 ); break; + case GRADE_BRONZE: table_writer_id( w, 0xc4927739aac4c591ull ); break; + case GRADE_SILVER: table_writer_id( w, 0xc3e51adeeaf12580ull ); break; + case GRADE_GOLD: table_writer_id( w, 0xc416c97e0d3218b3ull ); break; + default: return 0; /* no declared identity */ + } } - table_writer_put16( w, 0xd272 ); table_writer_put8( w, 7 ); /* grade */ - table_writer_put16( w, id_grade ); } - if ( value->grades_count < 0 || value->grades_count > 4 ) { return 0; } /* storage invariant */ - if ( value->grades_count > 0 ) - { - int64_t len_at_grades; - int32_t i; - table_writer_put16( w, 0x301a ); table_writer_put8( w, 14 ); /* grades */ - len_at_grades = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 7 ); table_writer_put32( w, (uint32_t) value->grades_count ); - for ( i = 0; i < value->grades_count; i++ ) + { /* grades */ + if ( value->grades_count < 0 || value->grades_count > 4 ) { return 0; } + if ( value->grades_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xd90a4e7682f799c5ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - uint16_t element_id = 0; - switch ( value->grades[i] ) /* Grade rides as the u16 hash of its VARIANT NAME (§5) */ - { - case GRADE_NONE: element_id = 0; break; - case GRADE_BRONZE: element_id = 0x3407; break; - case GRADE_SILVER: element_id = 0xa3e7; break; - case GRADE_GOLD: element_id = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ - } - table_writer_put16( w, element_id ); + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_loadout_config_wire_grades_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_loadout_config_wire_grades_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_loadout_config_wire_grades_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_grades, (uint32_t) ( w->offset - len_at_grades - 4 ) ); } - { - int all_default_podium = 1; - int32_t i; - for ( i = 0; i < 3; i++ ) { if ( value->podium[i] != GRADE_NONE ) { all_default_podium = 0; break; } } - if ( !all_default_podium ) + { /* podium */ + int rides = 0; int32_t i; + for ( i = 0; i < 3; i++ ) { if ( !( value->podium[i] == GRADE_NONE ) ) { rides = 1; break; } } + if ( rides ) { - int64_t len_at_podium; - table_writer_put16( w, 0x088a ); table_writer_put8( w, 14 ); /* podium (fixed [3]) */ - len_at_podium = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 7 ); table_writer_put32( w, 3 ); - for ( i = 0; i < 3; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb46ff45a23d72549ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - { - uint16_t element_id = 0; - switch ( value->podium[i] ) /* Grade rides as the u16 hash of its VARIANT NAME (§5) */ - { - case GRADE_NONE: element_id = 0; break; - case GRADE_BRONZE: element_id = 0x3407; break; - case GRADE_SILVER: element_id = 0xa3e7; break; - case GRADE_GOLD: element_id = 0xda27; break; - default: return 0; /* no variant names this value: no wire identity */ - } - table_writer_put16( w, element_id ); - } + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_loadout_config_wire_podium_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_loadout_config_wire_podium_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_loadout_config_wire_podium_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_podium, (uint32_t) ( w->offset - len_at_podium - 4 ) ); } } - if ( value->perks != 0 ) - { - table_writer_put16( w, 0x2fc5 ); table_writer_put8( w, 9 ); /* perks */ - table_writer_put64( w, (uint64_t) ( value->perks ) ); + { /* perks */ + if ( !( value->perks == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4b06f5847096e54aull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->perks ) ); + } } - { - int64_t body_primary = weapon_config_measure( &value->primary ); - if ( body_primary < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_primary > 2 ) /* all-default nested elides */ + { /* primary */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !weapon_config_save_body( &default_probe, &value->primary ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0x05f7 ); table_writer_put8( w, 13 ); /* primary */ - table_writer_put32( w, (uint32_t) body_primary ); - if ( !weapon_config_save_body( w, &value->primary ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbf31137ff783e939ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !weapon_config_save_body( w, &value->primary ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !weapon_config_save_body( &probe, &value->primary ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !weapon_config_save_body( w, &value->primary ) ) { return 0; } + } } } - { - int64_t len_at_backups; - int32_t i; - table_writer_put16( w, 0x1647 ); table_writer_put8( w, 14 ); /* backups (fixed [2]) */ - len_at_backups = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, 2 ); - for ( i = 0; i < 2; i++ ) + { /* backups */ + if ( 1 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xde28f0f5118acc24ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !weapon_config_save_body( w, &value->backups[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_loadout_config_wire_backups_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_loadout_config_wire_backups_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_loadout_config_wire_backups_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_backups, (uint32_t) ( w->offset - len_at_backups - 4 ) ); } - if ( value->attachments_count < 0 || value->attachments_count > 8 ) { return 0; } /* storage invariant */ - if ( value->attachments_count > 0 ) - { - int64_t len_at_attachments; - int32_t i; - table_writer_put16( w, 0x44d5 ); table_writer_put8( w, 14 ); /* attachments */ - len_at_attachments = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->attachments_count ); - for ( i = 0; i < value->attachments_count; i++ ) + { /* attachments */ + if ( value->attachments_count < 0 || value->attachments_count > 8 ) { return 0; } + if ( value->attachments_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xf901aa0340249a41ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_loadout_config_wire_attachments_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !attachment_save_body( w, &value->attachments[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_loadout_config_wire_attachments_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_loadout_config_wire_attachments_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_attachments, (uint32_t) ( w->offset - len_at_attachments - 4 ) ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t loadout_config_measure( const LoadoutConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !loadout_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t loadout_config_save( const LoadoutConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !loadout_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == loadout_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_load_body( TableReader * r, LoadoutConfig * value ) +static SCHEMA_UNUSED int loadout_config_load_body( TableReader * r, LoadoutConfig * value ) { - loadout_config_reset( value ); /* prefill declared defaults in place, then overlay */ + loadout_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xd272: /* grade */ + case 0x32a89b2977c48ad4ull: /* grade */ { - if ( kind != 7 ) + if ( kind != 30 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } { - uint16_t variant = table_reader_get16( &(*r) ); - switch ( variant ) + uint64_t variant_ref; + if ( !table_reader_leb( &(*r), &variant_ref ) || variant_ref > (*r).id_count ) { r->report->malformed = 1; return 0; } + if ( variant_ref == 0 ) { value->grade = GRADE_NONE; } + else { - case 0: value->grade = GRADE_NONE; break; - case 0x3407: value->grade = GRADE_BRONZE; break; - case 0xa3e7: value->grade = GRADE_SILVER; break; - case 0xda27: value->grade = GRADE_GOLD; break; - default: value->grade = GRADE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + switch ( table_reader_id_at( &(*r), variant_ref ) ) + { + case 0xc4927739aac4c591ull: value->grade = GRADE_BRONZE; break; + case 0xc3e51adeeaf12580ull: value->grade = GRADE_SILVER; break; + case 0xc416c97e0d3218b3ull: value->grade = GRADE_GOLD; break; + default: value->grade = GRADE_NONE; r->report->unknown++; break; + } } } break; } - case 0x301a: /* grades */ + case 0xd90a4e7682f799c5ull: /* grades */ { if ( kind != 14 ) { @@ -1261,50 +2516,50 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 7 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 30 ) + { + if ( !(table_kind_widens(elem_kind,30)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + int32_t previous_count = value->grades_count; + value->grades_count = 0; + for ( i = 0; i < kept; i++ ) { - uint16_t variant = table_reader_get16( &sub ); - switch ( variant ) { - case 0: value->grades[i] = GRADE_NONE; break; - case 0x3407: value->grades[i] = GRADE_BRONZE; break; - case 0xa3e7: value->grades[i] = GRADE_SILVER; break; - case 0xda27: value->grades[i] = GRADE_GOLD; break; - default: value->grades[i] = GRADE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + uint64_t variant_ref; + if ( !table_reader_leb( &sub, &variant_ref ) || variant_ref > sub.id_count ) { r->report->malformed = 1; goto end_grades; } + if ( variant_ref == 0 ) { value->grades[i] = GRADE_NONE; } + else + { + switch ( table_reader_id_at( &sub, variant_ref ) ) + { + case 0xc4927739aac4c591ull: value->grades[i] = GRADE_BRONZE; break; + case 0xc3e51adeeaf12580ull: value->grades[i] = GRADE_SILVER; break; + case 0xc416c97e0d3218b3ull: value->grades[i] = GRADE_GOLD; break; + default: value->grades[i] = GRADE_NONE; r->report->unknown++; break; + } + } } + value->grades_count = (int32_t) i + 1; } - decoded = i + 1; + end_grades: ; + { uint32_t tail; for (tail=(uint32_t)value->grades_count;tail<(uint32_t)previous_count && tail<4;tail++) { + memset(&value->grades[tail],0,sizeof(value->grades[tail])); + } } } - value->grades_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x088a: /* podium */ + case 0xb46ff45a23d72549ull: /* podium */ { if ( kind != 14 ) { @@ -1312,62 +2567,127 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - if ( elem_kind != 7 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 3 ) { keep = 3; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 30 ) + { + if ( !(table_kind_widens(elem_kind,30)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 3 ) { kept = 3; r->report->clamped++; } + for ( i = 0; i < kept; i++ ) { - uint16_t variant = table_reader_get16( &sub ); - switch ( variant ) { - case 0: value->podium[i] = GRADE_NONE; break; - case 0x3407: value->podium[i] = GRADE_BRONZE; break; - case 0xa3e7: value->podium[i] = GRADE_SILVER; break; - case 0xda27: value->podium[i] = GRADE_GOLD; break; - default: value->podium[i] = GRADE_NONE; r->report->unknown++; break; /* an id this build cannot name */ + uint64_t variant_ref; + if ( !table_reader_leb( &sub, &variant_ref ) || variant_ref > sub.id_count ) { r->report->malformed = 1; goto end_podium; } + if ( variant_ref == 0 ) { value->podium[i] = GRADE_NONE; } + else + { + switch ( table_reader_id_at( &sub, variant_ref ) ) + { + case 0xc4927739aac4c591ull: value->podium[i] = GRADE_BRONZE; break; + case 0xc3e51adeeaf12580ull: value->podium[i] = GRADE_SILVER; break; + case 0xc416c97e0d3218b3ull: value->podium[i] = GRADE_GOLD; break; + default: value->podium[i] = GRADE_NONE; r->report->unknown++; break; + } + } } } + end_podium: ; } } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x2fc5: /* perks */ + case 0x4b06f5847096e54aull: /* perks */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->perks = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->perks = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->perks = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->perks = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->perks = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->perks = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->perks = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->perks = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->perks = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x05f7: /* primary */ + case 0xbf31137ff783e939ull: /* primary */ { if ( kind != 13 ) { @@ -1375,18 +2695,13 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - weapon_config_load_body( &sub, &value->primary ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + weapon_config_load_body( &sub, &value->primary ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; weapon_config_reset( &value->primary ); } break; } - case 0x1647: /* backups */ + case 0xde28f0f5118acc24ull: /* backups */ { if ( kind != 14 ) { @@ -1394,44 +2709,33 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 2 ) { keep = 2; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) + { + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 2 ) { kept = 2; r->report->clamped++; } + for ( i = 0; i < kept; i++ ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_backups; } weapon_config_load_body( &elem, &value->backups[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; weapon_config_reset(&value->backups[i]); } } - sub.offset += elem_len; + end_backups: ; } } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x44d5: /* attachments */ + case 0xf901aa0340249a41ull: /* attachments */ { if ( kind != 14 ) { @@ -1439,219 +2743,650 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int loadout_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 8 ) { keep = 8; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) + { + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 8 ) { kept = 8; r->report->clamped++; } + int32_t previous_count = value->attachments_count; + value->attachments_count = 0; + for ( i = 0; i < kept; i++ ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_attachments; } attachment_load_body( &elem, &value->attachments[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; attachment_reset(&value->attachments[i]); } + value->attachments_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_attachments: ; + { uint32_t tail; for (tail=(uint32_t)value->attachments_count;tail<(uint32_t)previous_count && tail<8;tail++) { + attachment_reset(&value->attachments[tail]); + } } } - value->attachments_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int loadout_config_load( LoadoutConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + loadout_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return loadout_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t profile_config_measure( const ProfileConfig * value ) +static SCHEMA_UNUSED int loadout_config_save_message_body(TableBitWriter * w,const LoadoutConfig * value) { - int64_t bytes = 2; /* terminator */ - if ( value->name_length < 0 || value->name_length > 32 ) { return -1; } /* storage invariant */ - if ( value->name_length > 0 ) { bytes += 3 + 4 + value->name_length; } /* name */ - if ( value->icon_length < 0 || value->icon_length > 16 ) { return -1; } /* storage invariant */ - if ( value->icon_length > 0 ) { bytes += 3 + 4 + 5 + value->icon_length; } /* icon */ - if ( value->experience != 0 ) { bytes += 3 + 4; } /* experience */ - if ( value->tilt != 0 ) { bytes += 3 + 1; } /* tilt */ - if ( value->heading != 0 ) { bytes += 3 + 2; } /* heading */ - if ( value->timestamp != 0 ) { bytes += 3 + 8; } /* timestamp */ - if ( value->badge != 0 ) { bytes += 3 + 1; } /* badge */ - if ( value->port != 0 ) { bytes += 3 + 2; } /* port */ - if ( value->epoch != 0 ) { bytes += 3 + 8; } /* epoch */ - if ( value->precision != 0.0 ) { bytes += 3 + 8; } /* precision */ - { - int all_default_ratings = 1; - int32_t i; - for ( i = 0; i < 4; i++ ) { if ( value->ratings[i] != 0.0f ) { all_default_ratings = 0; break; } } - if ( !all_default_ratings ) - { - bytes += 3 + 4 + 5 + 16; /* ratings */ - } + (void)value; + { /* grade */ + if(!(value->grade == GRADE_SILVER)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,20,kTableMessageRefBitsHere); + switch(value->grade) { + case GRADE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case GRADE_BRONZE: table_bit_put(w,113,kTableMessageRefBitsHere); + break; + case GRADE_SILVER: table_bit_put(w,114,kTableMessageRefBitsHere); + break; + case GRADE_GOLD: table_bit_put(w,115,kTableMessageRefBitsHere); + break; + default: return 0; + } + } + } + { /* grades */ + if(value->grades_count<0 || value->grades_count>4) return 0; + if(value->grades_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,21,kTableMessageRefBitsHere); + if(value->grades_count<0 || value->grades_count>4) return 0; + table_bit_put(w,(uint64_t)value->grades_count-0,3); + { int32_t i; + for(i=0;igrades_count;i++) { + switch(value->grades[i]) { + case GRADE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case GRADE_BRONZE: table_bit_put(w,113,kTableMessageRefBitsHere); + break; + case GRADE_SILVER: table_bit_put(w,114,kTableMessageRefBitsHere); + break; + case GRADE_GOLD: table_bit_put(w,115,kTableMessageRefBitsHere); + break; + default: return 0; } - if ( value->has_loadout != 0 ) { bytes += 3 + 1; } /* has_loadout */ - if ( value->has_loadout ) - { - { - int64_t body_loadout = loadout_config_measure( &value->loadout ); - if ( body_loadout < 0 ) { return -1; } - if ( body_loadout > 2 ) { bytes += 3 + 4 + body_loadout; } /* loadout: all-default nested elides */ - } + } } + } + } + { /* podium */ + int rides=0; + { int32_t i; + for(i=0;i<3;i++) if(!(value->podium[i] == GRADE_NONE)) { rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,22,kTableMessageRefBitsHere); + if(3<0 || 3>3) return 0; + table_bit_put(w,(uint64_t)3-3,0); + { int32_t i; + for(i=0;i<3;i++) { + switch(value->podium[i]) { + case GRADE_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case GRADE_BRONZE: table_bit_put(w,113,kTableMessageRefBitsHere); + break; + case GRADE_SILVER: table_bit_put(w,114,kTableMessageRefBitsHere); + break; + case GRADE_GOLD: table_bit_put(w,115,kTableMessageRefBitsHere); + break; + default: return 0; } - return bytes; + } } + } + } + { /* perks */ + if(!(value->perks == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,23,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->perks)-UINT64_C(0),3); + } + } + { /* primary */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!weapon_config_save_message_body(&probe,&value->primary)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + if(!weapon_config_save_message_body(w,&value->primary)) return 0; + } + } + { /* backups */ + if(1) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,25,kTableMessageRefBitsHere); + if(2<0 || 2>2) return 0; + table_bit_put(w,(uint64_t)2-2,0); + { int32_t i; + for(i=0;i<2;i++) { + if(!weapon_config_save_message_body(w,&value->backups[i])) return 0; + } } + } + } + { /* attachments */ + if(value->attachments_count<0 || value->attachments_count>8) return 0; + if(value->attachments_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,26,kTableMessageRefBitsHere); + if(value->attachments_count<0 || value->attachments_count>8) return 0; + table_bit_put(w,(uint64_t)value->attachments_count-0,4); + { int32_t i; + for(i=0;iattachments_count;i++) { + if(!attachment_save_message_body(w,&value->attachments[i])) return 0; + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t loadout_config_measure_messages(const LoadoutConfig * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x32a89b2977c48ad4): { + if(!(entry->kind==30 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) value->grade=GRADE_NONE; + else switch(named->id) { + case UINT64_C(0xc4927739aac4c591): value->grade=GRADE_BRONZE; + break; + case UINT64_C(0xc3e51adeeaf12580): value->grade=GRADE_SILVER; + break; + case UINT64_C(0xc416c97e0d3218b3): value->grade=GRADE_GOLD; + break; + default:value->grade=GRADE_NONE; + r->report->unknown++; + break; + } } + break; + } + case UINT64_C(0xd90a4e7682f799c5): { + if(!(entry->kind==14 && entry->elem_kind==30)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;igrades[i] : &scratch; + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) (*item)=GRADE_NONE; + else switch(named->id) { + case UINT64_C(0xc4927739aac4c591): (*item)=GRADE_BRONZE; + break; + case UINT64_C(0xc3e51adeeaf12580): (*item)=GRADE_SILVER; + break; + case UINT64_C(0xc416c97e0d3218b3): (*item)=GRADE_GOLD; + break; + default:(*item)=GRADE_NONE; + r->report->unknown++; + break; + } } + }if(walkvalue_bits))goto malformed; + value->grades_count=(int32_t)kept; + } + break; + } + case UINT64_C(0xb46ff45a23d72549): { + if(!(entry->kind==14 && entry->elem_kind==30)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>3?3:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ipodium[i] : &scratch; + { const TableMessageEntry * named; + if(!table_message_ref(r,&named,1)) goto malformed; + if(named==NULL) (*item)=GRADE_NONE; + else switch(named->id) { + case UINT64_C(0xc4927739aac4c591): (*item)=GRADE_BRONZE; + break; + case UINT64_C(0xc3e51adeeaf12580): (*item)=GRADE_SILVER; + break; + case UINT64_C(0xc416c97e0d3218b3): (*item)=GRADE_GOLD; + break; + default:(*item)=GRADE_NONE; + r->report->unknown++; + break; + } } + }if(walkvalue_bits))goto malformed; + } + break; + } + case UINT64_C(0x4b06f5847096e54a): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->perks=(Perks)decoded; + } + break; + } + case UINT64_C(0xbf31137ff783e939): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!weapon_config_load_message_body(r,&value->primary))return 0; + break; + } + case UINT64_C(0xde28f0f5118acc24): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>2?2:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ibackups[i] : &scratch; + if(!weapon_config_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + } + break; + } + case UINT64_C(0xf901aa0340249a41): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>8?8:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iattachments[i] : &scratch; + if(!attachment_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->attachments_count=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int loadout_config_load_messages(LoadoutConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_tabledemo_profile_config_wire_name_( TableWriter * w, const ProfileConfig * value ) +{ + if ( value->name_length < 0 || value->name_length > 32 ) { return 0; } + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_profile_config_wire_icon_( TableWriter * w, const ProfileConfig * value ) +{ + if ( value->icon_length < 0 || value->icon_length > 16 ) { return 0; } + table_writer_put8( w, 6 ); table_writer_leb( w, (uint64_t) value->icon_length ); + table_writer_raw( w, value->icon, value->icon_length ); + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_save_body( TableWriter * w, const ProfileConfig * value ) +static SCHEMA_UNUSED int schema_tabledemo_profile_config_wire_ratings_( TableWriter * w, const ProfileConfig * value ) { - if ( value->name_length < 0 || value->name_length > 32 ) { return 0; } /* storage invariant */ - if ( value->name_length > 0 ) + int32_t i; + table_writer_put8( w, 10 ); table_writer_leb( w, (uint64_t) 4 ); + for ( i = 0; i < 4; i++ ) { - table_writer_put16( w, 0x30df ); table_writer_put8( w, 12 ); /* name */ - table_writer_put32( w, (uint32_t) value->name_length ); - table_writer_raw( w, value->name, value->name_length ); + table_writer_put32( w, table_float_to_bits( value->ratings[i] ) ); } - if ( value->icon_length < 0 || value->icon_length > 16 ) { return 0; } /* storage invariant */ - if ( value->icon_length > 0 ) - { - table_writer_put16( w, 0xf3b0 ); table_writer_put8( w, 14 ); /* icon */ - table_writer_put32( w, (uint32_t) ( 5 + value->icon_length ) ); - table_writer_put8( w, 6 ); table_writer_put32( w, (uint32_t) value->icon_length ); - table_writer_raw( w, value->icon, value->icon_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int profile_config_save_body( TableWriter * w, const ProfileConfig * value ) +{ + (void) value; + { /* name */ + if ( value->name_length < 0 || value->name_length > 32 ) { return 0; } + if ( value->name_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc4bcadba8e631b86ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_profile_config_wire_name_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_profile_config_wire_name_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_profile_config_wire_name_( w, value ) ) { return 0; } + } + } } - if ( value->experience != 0 ) - { - table_writer_put16( w, 0x61d8 ); table_writer_put8( w, 8 ); /* experience */ - table_writer_put32( w, (uint32_t) ( value->experience ) ); + { /* icon */ + if ( value->icon_length < 0 || value->icon_length > 16 ) { return 0; } + if ( value->icon_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xdbff4cc56c2092f0ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_profile_config_wire_icon_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_profile_config_wire_icon_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_profile_config_wire_icon_( w, value ) ) { return 0; } + } + } } - if ( value->tilt != 0 ) - { - table_writer_put16( w, 0x023c ); table_writer_put8( w, 2 ); /* tilt */ - table_writer_put8( w, (uint8_t) ( value->tilt ) ); + { /* experience */ + if ( !( value->experience == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xab8b6d521f80b969ull ); table_writer_put8( w, 8 ); + table_writer_put32( w, (uint32_t) ( value->experience ) ); + } } - if ( value->heading != 0 ) - { - table_writer_put16( w, 0x885d ); table_writer_put8( w, 3 ); /* heading */ - table_writer_put16( w, (uint16_t) ( value->heading ) ); + { /* tilt */ + if ( !( value->tilt == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1e5088ef2e9bafc6ull ); table_writer_put8( w, 2 ); + table_writer_put8( w, (uint8_t) ( value->tilt ) ); + } } - if ( value->timestamp != 0 ) - { - table_writer_put16( w, 0x67a0 ); table_writer_put8( w, 5 ); /* timestamp */ - table_writer_put64( w, (uint64_t) ( value->timestamp ) ); + { /* heading */ + if ( !( value->heading == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb128829190ca0f75ull ); table_writer_put8( w, 3 ); + table_writer_put16( w, (uint16_t) ( value->heading ) ); + } } - if ( value->badge != 0 ) - { - table_writer_put16( w, 0xb71c ); table_writer_put8( w, 6 ); /* badge */ - table_writer_put8( w, (uint8_t) ( value->badge ) ); + { /* timestamp */ + if ( !( value->timestamp == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5ddc92338ef53403ull ); table_writer_put8( w, 5 ); + table_writer_put64( w, (uint64_t) ( value->timestamp ) ); + } } - if ( value->port != 0 ) - { - table_writer_put16( w, 0x6942 ); table_writer_put8( w, 7 ); /* port */ - table_writer_put16( w, (uint16_t) ( value->port ) ); + { /* badge */ + if ( !( value->badge == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x10bda981fe095518ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->badge ) ); + } } - if ( value->epoch != 0 ) - { - table_writer_put16( w, 0xf4bd ); table_writer_put8( w, 9 ); /* epoch */ - table_writer_put64( w, (uint64_t) ( value->epoch ) ); + { /* port */ + if ( !( value->port == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x8c2cdb0da8933fa6ull ); table_writer_put8( w, 7 ); + table_writer_put16( w, (uint16_t) ( value->port ) ); + } } - if ( value->precision != 0.0 ) - { - table_writer_put16( w, 0x96e0 ); table_writer_put8( w, 11 ); /* precision */ - table_writer_put64( w, table_double_to_bits( value->precision ) ); + { /* epoch */ + if ( !( value->epoch == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xfc9697d1196e6ba6ull ); table_writer_put8( w, 9 ); + table_writer_put64( w, (uint64_t) ( value->epoch ) ); + } } - { - int all_default_ratings = 1; - int32_t i; - for ( i = 0; i < 4; i++ ) { if ( value->ratings[i] != 0.0f ) { all_default_ratings = 0; break; } } - if ( !all_default_ratings ) + { /* precision */ + if ( !( value->precision == 0.0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x288427f5babd05f7ull ); table_writer_put8( w, 11 ); + table_writer_put64( w, table_double_to_bits( value->precision ) ); + } + } + { /* ratings */ + int rides = 0; int32_t i; + for ( i = 0; i < 4; i++ ) { if ( !( value->ratings[i] == 0.0f ) ) { rides = 1; break; } } + if ( rides ) { - int64_t len_at_ratings; - table_writer_put16( w, 0x97dd ); table_writer_put8( w, 14 ); /* ratings (fixed [4]) */ - len_at_ratings = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 10 ); table_writer_put32( w, 4 ); - for ( i = 0; i < 4; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xb921eb8a3d0cb0f9ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_profile_config_wire_ratings_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - table_writer_put32( w, table_float_to_bits( value->ratings[i] ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_profile_config_wire_ratings_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_profile_config_wire_ratings_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_ratings, (uint32_t) ( w->offset - len_at_ratings - 4 ) ); } } - if ( value->has_loadout != 0 ) - { - table_writer_put16( w, 0xb4db ); table_writer_put8( w, 1 ); /* has_loadout */ - table_writer_put8( w, value->has_loadout ? 1 : 0 ); + { /* has_loadout */ + if ( !( value->has_loadout == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xa06f5e0a148e253cull ); table_writer_put8( w, 1 ); + table_writer_put8( w, value->has_loadout ? 1 : 0 ); + } } + { /* loadout */ if ( value->has_loadout ) { + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !loadout_config_save_body( &default_probe, &value->loadout ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - int64_t body_loadout = loadout_config_measure( &value->loadout ); - if ( body_loadout < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_loadout > 2 ) /* all-default nested elides */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5759ce7586bbb5a3ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !loadout_config_save_body( w, &value->loadout ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - table_writer_put16( w, 0x9f78 ); table_writer_put8( w, 13 ); /* loadout */ - table_writer_put32( w, (uint32_t) body_loadout ); + TableWriter probe = table_writer_probe( w ); + if ( !loadout_config_save_body( &probe, &value->loadout ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); if ( !loadout_config_save_body( w, &value->loadout ) ) { return 0; } } } } - table_writer_put16( w, 0 ); /* terminator */ + } + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t profile_config_measure( const ProfileConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !profile_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t profile_config_save( const ProfileConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !profile_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == profile_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( TableReader * r, ProfileConfig * value ) +static SCHEMA_UNUSED int profile_config_load_body( TableReader * r, ProfileConfig * value ) { - profile_config_reset( value ); /* prefill declared defaults in place, then overlay */ + profile_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x30df: /* name */ + case 0xc4bcadba8e631b86ull: /* name */ { if ( kind != 12 ) { @@ -1659,20 +3394,18 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 32 ) { keep = 32; r->report->clamped++; } - memcpy( value->name, r->buffer + r->offset, keep ); - value->name[keep] = 0; - value->name_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->name[0] = 0; value->name_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 32 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 32 ); r->report->clamped++; } + memcpy( value->name, sub.buffer, (size_t) keep ); value->name[keep] = 0; value->name_length = (int32_t) keep; break; } - case 0xf3b0: /* icon */ + case 0xdbff4cc56c2092f0ull: /* icon */ { if ( kind != 14 ) { @@ -1680,58 +3413,113 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 6 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 16 ) { keep = 16; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 6 ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); - value->icon[i] = decoded_v; + if ( !(table_kind_widens(elem_kind,6)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; } - decoded = i + 1; + kept = count; if ( kept > 16 ) { kept = 16; r->report->clamped++; } + value->icon_length = 0; + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 6: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_icon; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); + value->icon[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_icon; + } + value->icon_length = (int32_t) i + 1; + } + end_icon: ; } - value->icon_length = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x61d8: /* experience */ + case 0xab8b6d521f80b969ull: /* experience */ { if ( kind != 8 ) { + if ( table_kind_widens( kind, 8 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->experience = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->experience = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->experience = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); - value->experience = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->experience = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->experience = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + uint32_t decoded_v = (uint32_t) table_reader_get32( &(*r) ); + value->experience = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x023c: /* tilt */ + case 0x1e5088ef2e9bafc6ull: /* tilt */ { if ( kind != 2 ) { @@ -1739,44 +3527,160 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); - value->tilt = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + int8_t decoded_v = (int8_t) table_reader_get8( &(*r) ); + value->tilt = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x885d: /* heading */ + case 0xb128829190ca0f75ull: /* heading */ { if ( kind != 3 ) { + if ( table_kind_widens( kind, 3 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->heading = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + value->heading = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); - value->heading = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->heading = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + int16_t decoded_v = (int16_t) table_reader_get16( &(*r) ); + value->heading = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x67a0: /* timestamp */ + case 0x5ddc92338ef53403ull: /* timestamp */ { if ( kind != 5 ) { + if ( table_kind_widens( kind, 5 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->timestamp = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->timestamp = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + value->timestamp = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + value->timestamp = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); - value->timestamp = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->timestamp = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->timestamp = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int32_t) table_reader_get32( &(*r) ); + value->timestamp = decoded_wide; + break; + } + case 5: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + int64_t decoded_v = (int64_t) table_reader_get64( &(*r) ); + value->timestamp = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xb71c: /* badge */ + case 0x10bda981fe095518ull: /* badge */ { if ( kind != 6 ) { @@ -1784,56 +3688,209 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->badge = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->badge = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x6942: /* port */ + case 0x8c2cdb0da8933fa6ull: /* port */ { if ( kind != 7 ) { + if ( table_kind_widens( kind, 7 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->port = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->port = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); - value->port = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->port = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &(*r) ); + value->port = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xf4bd: /* epoch */ + case 0xfc9697d1196e6ba6ull: /* epoch */ { if ( kind != 9 ) { + if ( table_kind_widens( kind, 9 ) ) + { + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->epoch = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->epoch = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->epoch = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->epoch = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); - value->epoch = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &(*r) ); + value->epoch = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint16_t) table_reader_get16( &(*r) ); + value->epoch = decoded_wide; + break; + } + case 8: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + uint64_t decoded_wide = (uint32_t) table_reader_get32( &(*r) ); + value->epoch = decoded_wide; + break; + } + case 9: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + { + uint64_t decoded_v = (uint64_t) table_reader_get64( &(*r) ); + value->epoch = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x96e0: /* precision */ + case 0x288427f5babd05f7ull: /* precision */ { if ( kind != 11 ) { + if ( table_kind_widens( kind, 11 ) ) + { + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->precision = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->precision = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } - value->precision = table_bits_to_double( table_reader_get64( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + double decoded_wide = table_wire_widen_f32( table_reader_get32( &(*r) ) ); + value->precision = decoded_wide; + break; + } + case 11: + { + if ( !table_reader_has( &(*r), 8 ) ) { r->report->malformed = 1; return 0; } + value->precision = table_bits_to_double( table_reader_get64( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x97dd: /* ratings */ + case 0xb921eb8a3d0cb0f9ull: /* ratings */ { if ( kind != 14 ) { @@ -1841,37 +3898,39 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - if ( elem_kind != 10 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - value->ratings[i] = table_bits_to_float( table_reader_get32( &sub ) ); + if ( elem_kind != 10 ) + { + if ( !(table_kind_widens(elem_kind,10)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 10: + { + if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; goto end_ratings; } + value->ratings[i] = table_bits_to_float( table_reader_get32( &sub ) ); + break; + } + default: r->report->malformed = 1; goto end_ratings; + } + } + end_ratings: ; } } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0xb4db: /* has_loadout */ + case 0xa06f5e0a148e253cull: /* has_loadout */ { if ( kind != 1 ) { @@ -1879,11 +3938,19 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } - value->has_loadout = table_reader_get8( &(*r) ) != 0; + switch ( kind ) + { + case 1: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + value->has_loadout = table_reader_get8( &(*r) ) != 0; + break; + } + default: r->report->malformed = 1; return 0; + } break; } - case 0x9f78: /* loadout */ + case 0x5759ce7586bbb5a3ull: /* loadout */ { if ( kind != 13 ) { @@ -1891,141 +3958,641 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int profile_config_load_body( if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - loadout_config_load_body( &sub, &value->loadout ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + loadout_config_load_body( &sub, &value->loadout ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; loadout_config_reset( &value->loadout ); } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int profile_config_load( ProfileConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + profile_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return profile_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t root_config_measure( const RootConfig * value ) +static SCHEMA_UNUSED int profile_config_save_message_body(TableBitWriter * w,const ProfileConfig * value) +{ + (void)value; + { /* name */ + if(value->name_length<0 || value->name_length>32) return 0; + if(value->name_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,38,kTableMessageRefBitsHere); + if(value->name_length<0 || value->name_length>32) return 0; + table_bit_put(w,(uint64_t)value->name_length,6); + table_bit_align_write(w); + table_bit_putbytes(w,value->name,value->name_length); + } + } + { /* icon */ + if(value->icon_length<0 || value->icon_length>16) return 0; + if(value->icon_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,39,kTableMessageRefBitsHere); + if(value->icon_length<0 || value->icon_length>16) return 0; + table_bit_put(w,(uint64_t)value->icon_length,5); + table_bit_align_write(w); + table_bit_putbytes(w,value->icon,value->icon_length); + } + } + { /* experience */ + if(!(value->experience == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,40,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->experience)-UINT64_C(0),32); + } + } + { /* tilt */ + if(!(value->tilt == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,41,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->tilt)-UINT64_C(0),8); + } + } + { /* heading */ + if(!(value->heading == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,42,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->heading)-UINT64_C(0),16); + } + } + { /* timestamp */ + if(!(value->timestamp == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,43,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->timestamp)-UINT64_C(0),64); + } + } + { /* badge */ + if(!(value->badge == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,44,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->badge)-UINT64_C(0),8); + } + } + { /* port */ + if(!(value->port == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,45,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->port)-UINT64_C(0),16); + } + } + { /* epoch */ + if(!(value->epoch == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,46,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->epoch)-UINT64_C(0),64); + } + } + { /* precision */ + if(!(value->precision == 0.0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,47,kTableMessageRefBitsHere); + table_bit_put(w,table_double_to_bits(value->precision),64); + } + } + { /* ratings */ + int rides=0; + { int32_t i; + for(i=0;i<4;i++) if(!(value->ratings[i] == 0.0f)) { rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,48,kTableMessageRefBitsHere); + if(4<0 || 4>4) return 0; + table_bit_put(w,(uint64_t)4-4,0); + { int32_t i; + for(i=0;i<4;i++) { + table_bit_put(w,table_float_to_bits(value->ratings[i]),32); + } } + } + } + { /* has_loadout */ + if(!(value->has_loadout == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,49,kTableMessageRefBitsHere); + table_bit_put(w,value->has_loadout ? 1 : 0,1); + } + } + { /* loadout */ + if(value->has_loadout) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!loadout_config_save_message_body(&probe,&value->loadout)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,50,kTableMessageRefBitsHere); + if(!loadout_config_save_message_body(w,&value->loadout)) return 0; + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t profile_config_measure_messages(const ProfileConfig * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,32); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xdbff4cc56c2092f0): { + if(!(entry->kind==14 && entry->elem_kind==6)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,14)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(entry->min,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + kept=n>16 ? 16 : n; + if(!table_bit_getbytes(&r->bits,value->icon,(int64_t)kept)||!table_message_skip_run(r,n-kept,8))goto malformed; + if(keptreport->clamped++; + value->icon_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xab8b6d521f80b969): { + if(!(entry->kind==8 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,8)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>4294967295ull){decoded=4294967295ull; + r->report->clamped++; + } + value->experience=(uint32_t)decoded; + } + break; + } + case UINT64_C(0x1e5088ef2e9bafc6): { + if(!(entry->kind==2 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,2)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-128ll){decoded=-128ll; + r->report->clamped++; + } + if(decoded>127ll){decoded=127ll; + r->report->clamped++; + } + value->tilt=(int8_t)decoded; + } + break; + } + case UINT64_C(0xb128829190ca0f75): { + if(!(entry->kind==3 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,3)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-32768ll){decoded=-32768ll; + r->report->clamped++; + } + if(decoded>32767ll){decoded=32767ll; + r->report->clamped++; + } + value->heading=(int16_t)decoded; + } + break; + } + case UINT64_C(0x5ddc92338ef53403): { + if(!(entry->kind==5 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,5)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + value->timestamp=(int64_t)decoded; + } + break; + } + case UINT64_C(0x10bda981fe095518): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->badge=(uint8_t)decoded; + } + break; + } + case UINT64_C(0x8c2cdb0da8933fa6): { + if(!(entry->kind==7 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>65535ull){decoded=65535ull; + r->report->clamped++; + } + value->port=(uint16_t)decoded; + } + break; + } + case UINT64_C(0xfc9697d1196e6ba6): { + if(!(entry->kind==9 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,9)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + value->epoch=(uint64_t)decoded; + } + break; + } + case UINT64_C(0x288427f5babd05f7): { + if(!(entry->kind==11 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,11)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { double decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=table_wire_widen_f32(table_float_to_bits(narrow)); + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(double)table_bits_to_double(raw); + } + value->precision=decoded; + } + break; + } + case UINT64_C(0xb921eb8a3d0cb0f9): { + if(!(entry->kind==14 && entry->elem_kind==10)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iratings[i] : &scratch; + { float decoded; + if(element_entry->kind==10) {float narrow; + if(!table_message_float(r,element_entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + (*item)=decoded; + } + }if(walkvalue_bits))goto malformed; + } + break; + } + case UINT64_C(0xa06f5e0a148e253c): { + if(!(entry->kind==1 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,1)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + value->has_loadout=lo!=0; + } + break; + } + case UINT64_C(0x5759ce7586bbb5a3): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!loadout_config_load_message_body(r,&value->loadout))return 0; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int profile_config_load_messages(ProfileConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_tabledemo_root_config_wire_version_note_( TableWriter * w, const RootConfig * value ) +{ + if ( value->version_note_length < 0 || value->version_note_length > 16 ) { return 0; } + table_writer_raw( w, value->version_note, value->version_note_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_root_config_wire_weapons_( TableWriter * w, const RootConfig * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->version_note_length < 0 || value->version_note_length > 16 ) { return -1; } /* storage invariant */ - if ( value->version_note_length > 0 ) { bytes += 3 + 4 + value->version_note_length; } /* version_note */ - if ( value->weapons_count < 0 || value->weapons_count > 8 ) { return -1; } /* storage invariant */ - if ( value->weapons_count > 0 ) + if ( value->weapons_count < 0 || value->weapons_count > 8 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->weapons_count ); + for ( i = 0; i < value->weapons_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* weapons */ - for ( i = 0; i < value->weapons_count; i++ ) + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !weapon_config_save_body( w, &value->weapons[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_weapons = weapon_config_measure( &value->weapons[i] ); - if ( elem_weapons < 0 ) { return -1; } - bytes += 4 + elem_weapons; + TableWriter probe = table_writer_probe( w ); + if ( !weapon_config_save_body( &probe, &value->weapons[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !weapon_config_save_body( w, &value->weapons[i] ) ) { return 0; } } } - if ( value->profiles_count < 0 || value->profiles_count > 4 ) { return -1; } /* storage invariant */ - if ( value->profiles_count > 0 ) + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_root_config_wire_profiles_( TableWriter * w, const RootConfig * value ) +{ + if ( value->profiles_count < 0 || value->profiles_count > 4 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->profiles_count ); + for ( i = 0; i < value->profiles_count; i++ ) { - int32_t i; - bytes += 3 + 4 + 5; /* profiles */ - for ( i = 0; i < value->profiles_count; i++ ) + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !profile_config_save_body( w, &value->profiles[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_profiles = profile_config_measure( &value->profiles[i] ); - if ( elem_profiles < 0 ) { return -1; } - bytes += 4 + elem_profiles; + TableWriter probe = table_writer_probe( w ); + if ( !profile_config_save_body( &probe, &value->profiles[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !profile_config_save_body( w, &value->profiles[i] ) ) { return 0; } } } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int root_config_save_body( TableWriter * w, const RootConfig * value ) +static SCHEMA_UNUSED int root_config_save_body( TableWriter * w, const RootConfig * value ) { - if ( value->version_note_length < 0 || value->version_note_length > 16 ) { return 0; } /* storage invariant */ - if ( value->version_note_length > 0 ) - { - table_writer_put16( w, 0xe726 ); table_writer_put8( w, 12 ); /* version_note */ - table_writer_put32( w, (uint32_t) value->version_note_length ); - table_writer_raw( w, value->version_note, value->version_note_length ); + (void) value; + { /* version_note */ + if ( value->version_note_length < 0 || value->version_note_length > 16 ) { return 0; } + if ( value->version_note_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x8a67c9728746d394ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_root_config_wire_version_note_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_root_config_wire_version_note_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_root_config_wire_version_note_( w, value ) ) { return 0; } + } + } } - if ( value->weapons_count < 0 || value->weapons_count > 8 ) { return 0; } /* storage invariant */ - if ( value->weapons_count > 0 ) - { - int64_t len_at_weapons; - int32_t i; - table_writer_put16( w, 0x91cd ); table_writer_put8( w, 14 ); /* weapons */ - len_at_weapons = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->weapons_count ); - for ( i = 0; i < value->weapons_count; i++ ) + { /* weapons */ + if ( value->weapons_count < 0 || value->weapons_count > 8 ) { return 0; } + if ( value->weapons_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x41cbd901b87fabb6ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_root_config_wire_weapons_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !weapon_config_save_body( w, &value->weapons[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_root_config_wire_weapons_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_root_config_wire_weapons_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_weapons, (uint32_t) ( w->offset - len_at_weapons - 4 ) ); } - if ( value->profiles_count < 0 || value->profiles_count > 4 ) { return 0; } /* storage invariant */ - if ( value->profiles_count > 0 ) - { - int64_t len_at_profiles; - int32_t i; - table_writer_put16( w, 0x73fd ); table_writer_put8( w, 14 ); /* profiles */ - len_at_profiles = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->profiles_count ); - for ( i = 0; i < value->profiles_count; i++ ) + { /* profiles */ + if ( value->profiles_count < 0 || value->profiles_count > 4 ) { return 0; } + if ( value->profiles_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x8181e61fc0436767ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_root_config_wire_profiles_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !profile_config_save_body( w, &value->profiles[i] ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_root_config_wire_profiles_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_root_config_wire_profiles_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_profiles, (uint32_t) ( w->offset - len_at_profiles - 4 ) ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t root_config_measure( const RootConfig * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !root_config_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t root_config_save( const RootConfig * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !root_config_save_body( &w, value ) ) { return -1; } - return w.offset; /* == root_config_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int root_config_load_body( TableReader * r, RootConfig * value ) +static SCHEMA_UNUSED int root_config_load_body( TableReader * r, RootConfig * value ) { - root_config_reset( value ); /* prefill declared defaults in place, then overlay */ + root_config_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xe726: /* version_note */ + case 0x8a67c9728746d394ull: /* version_note */ { if ( kind != 12 ) { @@ -2033,20 +4600,18 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int root_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 16 ) { keep = 16; r->report->clamped++; } - memcpy( value->version_note, r->buffer + r->offset, keep ); - value->version_note[keep] = 0; - value->version_note_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->version_note[0] = 0; value->version_note_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 16 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 16 ); r->report->clamped++; } + memcpy( value->version_note, sub.buffer, (size_t) keep ); value->version_note[keep] = 0; value->version_note_length = (int32_t) keep; break; } - case 0x91cd: /* weapons */ + case 0x41cbd901b87fabb6ull: /* weapons */ { if ( kind != 14 ) { @@ -2054,47 +4619,39 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int root_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 8 ) { keep = 8; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) + { + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 8 ) { kept = 8; r->report->clamped++; } + int32_t previous_count = value->weapons_count; + value->weapons_count = 0; + for ( i = 0; i < kept; i++ ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_weapons; } weapon_config_load_body( &elem, &value->weapons[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; weapon_config_reset(&value->weapons[i]); } + value->weapons_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_weapons: ; + { uint32_t tail; for (tail=(uint32_t)value->weapons_count;tail<(uint32_t)previous_count && tail<8;tail++) { + weapon_config_reset(&value->weapons[tail]); + } } } - value->weapons_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0x73fd: /* profiles */ + case 0x8181e61fc0436767ull: /* profiles */ { if ( kind != 14 ) { @@ -2102,128 +4659,385 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int root_config_load_body( Ta if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + int32_t previous_count = value->profiles_count; + value->profiles_count = 0; + for ( i = 0; i < kept; i++ ) + { + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_profiles; } profile_config_load_body( &elem, &value->profiles[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; profile_config_reset(&value->profiles[i]); } + value->profiles_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_profiles: ; + { uint32_t tail; for (tail=(uint32_t)value->profiles_count;tail<(uint32_t)previous_count && tail<4;tail++) { + profile_config_reset(&value->profiles[tail]); + } } } - value->profiles_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int root_config_load( RootConfig * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + root_config_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return root_config_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t attachment_measure( const Attachment * value ) +static SCHEMA_UNUSED int root_config_save_message_body(TableBitWriter * w,const RootConfig * value) { - int64_t bytes = 2; /* terminator */ - if ( value->slot != 0 ) { bytes += 3 + 4; } /* slot */ - if ( value->power != 1.0f ) { bytes += 3 + 4; } /* power */ - return bytes; + (void)value; + { /* version_note */ + if(value->version_note_length<0 || value->version_note_length>16) return 0; + if(value->version_note_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,91,kTableMessageRefBitsHere); + if(value->version_note_length<0 || value->version_note_length>16) return 0; + table_bit_put(w,(uint64_t)value->version_note_length,5); + table_bit_align_write(w); + table_bit_putbytes(w,value->version_note,value->version_note_length); + } + } + { /* weapons */ + if(value->weapons_count<0 || value->weapons_count>8) return 0; + if(value->weapons_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,92,kTableMessageRefBitsHere); + if(value->weapons_count<0 || value->weapons_count>8) return 0; + table_bit_put(w,(uint64_t)value->weapons_count-0,4); + { int32_t i; + for(i=0;iweapons_count;i++) { + if(!weapon_config_save_message_body(w,&value->weapons[i])) return 0; + } } + } + } + { /* profiles */ + if(value->profiles_count<0 || value->profiles_count>4) return 0; + if(value->profiles_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,93,kTableMessageRefBitsHere); + if(value->profiles_count<0 || value->profiles_count>4) return 0; + table_bit_put(w,(uint64_t)value->profiles_count-0,3); + { int32_t i; + for(i=0;iprofiles_count;i++) { + if(!profile_config_save_message_body(w,&value->profiles[i])) return 0; + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int attachment_save_body( TableWriter * w, const Attachment * value ) +static SCHEMA_UNUSED int64_t root_config_measure_messages(const RootConfig * values,int64_t count,TableReport * report) { - if ( value->slot != 0 ) - { - table_writer_put16( w, 0x37e4 ); table_writer_put8( w, 4 ); /* slot */ - table_writer_put32( w, (uint32_t) ( value->slot ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x8a67c9728746d394): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,16); + memcpy(value->version_note,text,(size_t)kept); + value->version_note[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->version_note_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x41cbd901b87fabb6): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>8?8:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iweapons[i] : &scratch; + if(!weapon_config_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->weapons_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x8181e61fc0436767): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;iprofiles[i] : &scratch; + if(!profile_config_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->profiles_count=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int root_config_load_messages(RootConfig * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int attachment_save_body( TableWriter * w, const Attachment * value ) +{ + (void) value; + { /* slot */ + if ( !( value->slot == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x6a771618f6fe31d1ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->slot ) ); + } } - if ( value->power != 1.0f ) - { - table_writer_put16( w, 0xd609 ); table_writer_put8( w, 10 ); /* power */ - table_writer_put32( w, table_float_to_bits( value->power ) ); + { /* power */ + if ( !( value->power == 1.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xeef9d1358ae7b4e6ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->power ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t attachment_measure( const Attachment * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !attachment_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t attachment_save( const Attachment * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !attachment_save_body( &w, value ) ) { return -1; } - return w.offset; /* == attachment_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int attachment_load_body( TableReader * r, Attachment * value ) +static SCHEMA_UNUSED int attachment_load_body( TableReader * r, Attachment * value ) { - attachment_reset( value ); /* prefill declared defaults in place, then overlay */ + attachment_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x37e4: /* slot */ + case 0x6a771618f6fe31d1ull: /* slot */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 7ll ) { decoded_wide = 7ll; r->report->clamped++; } + value->slot = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 7ll ) { decoded_wide = 7ll; r->report->clamped++; } + value->slot = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 7 ) { decoded_v = 7; r->report->clamped++; } + value->slot = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 7 ) { decoded_v = 7; r->report->clamped++; } - value->slot = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 7ll ) { decoded_wide = 7ll; r->report->clamped++; } + value->slot = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 7ll ) { decoded_wide = 7ll; r->report->clamped++; } + value->slot = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 7 ) { decoded_v = 7; r->report->clamped++; } + value->slot = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xd609: /* power */ + case 0xeef9d1358ae7b4e6ull: /* power */ { if ( kind != 10 ) { @@ -2231,69 +5045,223 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int attachment_load_body( Tab if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->power = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->power = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int attachment_load( Attachment * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + attachment_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return attachment_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t buff_measure( const Buff * value ) +static SCHEMA_UNUSED int attachment_save_message_body(TableBitWriter * w,const Attachment * value) { - int64_t bytes = 2; /* terminator */ - if ( value->multiplier != 1.0f ) { bytes += 3 + 4; } /* multiplier */ - return bytes; + (void)value; + { /* slot */ + if(!(value->slot == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,3,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->slot)-UINT64_C(0),3); + } + } + { /* power */ + if(!(value->power == 1.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,4,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->power),32); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int buff_save_body( TableWriter * w, const Buff * value ) +static SCHEMA_UNUSED int64_t attachment_measure_messages(const Attachment * values,int64_t count,TableReport * report) { - if ( value->multiplier != 1.0f ) - { - table_writer_put16( w, 0x32e0 ); table_writer_put8( w, 10 ); /* multiplier */ - table_writer_put32( w, table_float_to_bits( value->multiplier ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x6a771618f6fe31d1): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>7ll){decoded=7ll; + r->report->clamped++; + } + value->slot=(int32_t)decoded; + } + break; + } + case UINT64_C(0xeef9d1358ae7b4e6): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->power=decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int attachment_load_messages(Attachment * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int buff_save_body( TableWriter * w, const Buff * value ) +{ + (void) value; + { /* multiplier */ + if ( !( value->multiplier == 1.0f ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x9adc623a805c87c6ull ); table_writer_put8( w, 10 ); + table_writer_put32( w, table_float_to_bits( value->multiplier ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t buff_measure( const Buff * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !buff_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t buff_save( const Buff * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !buff_save_body( &w, value ) ) { return -1; } - return w.offset; /* == buff_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int buff_load_body( TableReader * r, Buff * value ) +static SCHEMA_UNUSED int buff_load_body( TableReader * r, Buff * value ) { - buff_reset( value ); /* prefill declared defaults in place, then overlay */ + buff_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x32e0: /* multiplier */ + case 0x9adc623a805c87c6ull: /* multiplier */ { if ( kind != 10 ) { @@ -2301,104 +5269,474 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int buff_load_body( TableRead if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } - value->multiplier = table_bits_to_float( table_reader_get32( &(*r) ) ); + switch ( kind ) + { + case 10: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + value->multiplier = table_bits_to_float( table_reader_get32( &(*r) ) ); + break; + } + default: r->report->malformed = 1; return 0; + } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int buff_load( Buff * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + buff_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return buff_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t debuff_measure( const Debuff * value ) +static SCHEMA_UNUSED int buff_save_message_body(TableBitWriter * w,const Buff * value) { - int64_t bytes = 2; /* terminator */ - if ( value->amount != 0 ) { bytes += 3 + 4; } /* amount */ - return bytes; + (void)value; + { /* multiplier */ + if(!(value->multiplier == 1.0f)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,5,kTableMessageRefBitsHere); + table_bit_put(w,table_float_to_bits(value->multiplier),32); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int debuff_save_body( TableWriter * w, const Debuff * value ) +static SCHEMA_UNUSED int64_t buff_measure_messages(const Buff * values,int64_t count,TableReport * report) { - if ( value->amount != 0 ) - { - table_writer_put16( w, 0x39cc ); table_writer_put8( w, 4 ); /* amount */ - table_writer_put32( w, (uint32_t) ( value->amount ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x9adc623a805c87c6): { + if(!(entry->kind==10 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,10)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { float decoded; + if(entry->kind==10) {float narrow; + if(!table_message_float(r,entry,&narrow))goto malformed; + decoded=narrow; + } else {uint64_t raw; + if(!table_bit_get(&r->bits,&raw,64))goto malformed; + decoded=(float)table_bits_to_double(raw); + } + value->multiplier=decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int buff_load_messages(Buff * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int debuff_save_body( TableWriter * w, const Debuff * value ) +{ + (void) value; + { /* amount */ + if ( !( value->amount == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x8113fe7ea2b16969ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->amount ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t debuff_measure( const Debuff * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !debuff_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t debuff_save( const Debuff * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !debuff_save_body( &w, value ) ) { return -1; } - return w.offset; /* == debuff_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int debuff_load_body( TableReader * r, Debuff * value ) +static SCHEMA_UNUSED int debuff_load_body( TableReader * r, Debuff * value ) { - debuff_reset( value ); /* prefill declared defaults in place, then overlay */ + debuff_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x39cc: /* amount */ + case 0x8113fe7ea2b16969ull: /* amount */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100ll ) { decoded_wide = 100ll; r->report->clamped++; } + value->amount = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100ll ) { decoded_wide = 100ll; r->report->clamped++; } + value->amount = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 100 ) { decoded_v = 100; r->report->clamped++; } + value->amount = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 100 ) { decoded_v = 100; r->report->clamped++; } - value->amount = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100ll ) { decoded_wide = 100ll; r->report->clamped++; } + value->amount = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 100ll ) { decoded_wide = 100ll; r->report->clamped++; } + value->amount = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 100 ) { decoded_v = 100; r->report->clamped++; } + value->amount = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int debuff_load( Debuff * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + debuff_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return debuff_load_body( &r, value ); } +static SCHEMA_UNUSED int debuff_save_message_body(TableBitWriter * w,const Debuff * value) +{ + (void)value; + { /* amount */ + if(!(value->amount == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,6,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->amount)-UINT64_C(0),7); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t debuff_measure_messages(const Debuff * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x8113fe7ea2b16969): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>100ll){decoded=100ll; + r->report->clamped++; + } + value->amount=(int32_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int debuff_load_messages(Debuff * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define weapon_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define weapon_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define weapon_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define weapon_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define weapon_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define loadout_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define loadout_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define loadout_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define loadout_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define loadout_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define profile_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define profile_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define profile_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define profile_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define profile_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define root_config_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define root_config_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define root_config_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define root_config_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define root_config_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define attachment_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define attachment_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define attachment_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define attachment_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define attachment_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define buff_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define buff_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define buff_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define buff_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define buff_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define debuff_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define debuff_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define debuff_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define debuff_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define debuff_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* weapon_config_open: match the header and POINT. On a match the bytes ARE what this @@ -2420,6 +5758,8 @@ static SCHEMA_UNUSED int debuff_load( Debuff * value, const uint8_t * buffer, in file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const WeaponConfig * weapon_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const WeaponConfig *)table_cook_open_ex(bytes,length,sizeof(WeaponConfig),SCHEMA_TABLE_ALIGNOF(WeaponConfig),reason); } static SCHEMA_UNUSED const WeaponConfig * weapon_config_open( const void * bytes, uint64_t length ) { return (const WeaponConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( WeaponConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( WeaponConfig ) ); @@ -2444,6 +5784,8 @@ static SCHEMA_UNUSED const WeaponConfig * weapon_config_open( const void * bytes file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const LoadoutConfig * loadout_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const LoadoutConfig *)table_cook_open_ex(bytes,length,sizeof(LoadoutConfig),SCHEMA_TABLE_ALIGNOF(LoadoutConfig),reason); } static SCHEMA_UNUSED const LoadoutConfig * loadout_config_open( const void * bytes, uint64_t length ) { return (const LoadoutConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( LoadoutConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( LoadoutConfig ) ); @@ -2468,6 +5810,8 @@ static SCHEMA_UNUSED const LoadoutConfig * loadout_config_open( const void * byt file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const ProfileConfig * profile_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const ProfileConfig *)table_cook_open_ex(bytes,length,sizeof(ProfileConfig),SCHEMA_TABLE_ALIGNOF(ProfileConfig),reason); } static SCHEMA_UNUSED const ProfileConfig * profile_config_open( const void * bytes, uint64_t length ) { return (const ProfileConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( ProfileConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( ProfileConfig ) ); @@ -2492,11 +5836,158 @@ static SCHEMA_UNUSED const ProfileConfig * profile_config_open( const void * byt file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const RootConfig * root_config_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const RootConfig *)table_cook_open_ex(bytes,length,sizeof(RootConfig),SCHEMA_TABLE_ALIGNOF(RootConfig),reason); } static SCHEMA_UNUSED const RootConfig * root_config_open( const void * bytes, uint64_t length ) { return (const RootConfig *) table_cook_open( bytes, length, (uint64_t) sizeof( RootConfig ), (uint64_t) SCHEMA_TABLE_ALIGNOF( RootConfig ) ); } +static SCHEMA_UNUSED int schema_tabledemo_weapon_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const WeaponConfig * value=(const WeaponConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + { uint32_t bits; memcpy(&bits,&value->damage,4); table_cook_put(at+0,bits,4,order); } + { uint32_t bits; memcpy(&bits,&value->speed,4); table_cook_put(at+4,bits,4,order); } + table_cook_put(at+8,(uint64_t)value->penetration,4,order); + table_cook_put(at+12,(uint64_t)value->channel,4,order); + table_cook_put(at+16,value->homing ? 1 : 0,1,order); + table_cook_put(at+20,(uint64_t)value->effect.type,1,order); + switch(value->effect.type) { + case EFFECT_TYPE_BUFF: { + if(!schema_tabledemo_buff_cook_body_(n,at+20+4,&value->effect.as.buff,order)) { return 0; } + break; } + case EFFECT_TYPE_DEBUFF: { + if(!schema_tabledemo_debuff_cook_body_(n,at+20+4,&value->effect.as.debuff,order)) { return 0; } + break; } + default: break; + } + return 1; +} +static SCHEMA_UNUSED int64_t weapon_config_cook_measure(const WeaponConfig * value) { (void)value; return 112; } +static SCHEMA_UNUSED int weapon_config_cook(const WeaponConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<112) { return 0; } + memset(raw,0,112); + if(!schema_tabledemo_weapon_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+104,UINT64_C(0x0577780d86c4b0c3),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,32,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_loadout_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const LoadoutConfig * value=(const LoadoutConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->grade,1,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<4;cook_i1++) { + table_cook_put(at+1+cook_i1*1,(uint64_t)value->grades[cook_i1],1,order); + } } + table_cook_put(at+1+7,(uint32_t)value->grades_count,4,order); + { int32_t cook_i2; for(cook_i2=0;cook_i2<3;cook_i2++) { + table_cook_put(at+12+cook_i2*1,(uint64_t)value->podium[cook_i2],1,order); + } } + table_cook_put(at+16,(uint64_t)value->perks,8,order); + if(!schema_tabledemo_weapon_config_cook_body_(n,at+24,&value->primary,order)) { return 0; } + { int32_t cook_i3; for(cook_i3=0;cook_i3<2;cook_i3++) { + if(!schema_tabledemo_weapon_config_cook_body_(n,at+52+cook_i3*28,&value->backups[cook_i3],order)) { return 0; } + } } + { int32_t cook_i4; for(cook_i4=0;cook_i4<8;cook_i4++) { + if(!schema_tabledemo_attachment_cook_body_(n,at+108+cook_i4*8,&value->attachments[cook_i4],order)) { return 0; } + } } + table_cook_put(at+108+64,(uint32_t)value->attachments_count,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t loadout_config_cook_measure(const LoadoutConfig * value) { (void)value; return 256; } +static SCHEMA_UNUSED int loadout_config_cook(const LoadoutConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<256) { return 0; } + memset(raw,0,256); + if(!schema_tabledemo_loadout_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+248,UINT64_C(0xa6aa0caea28dcca1),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,176,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_profile_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const ProfileConfig * value=(const ProfileConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->name,value->name_length,33); + table_cook_put(at+0+36,(uint32_t)value->name_length,4,order); + table_cook_bytes(at+40,value->icon,value->icon_length,16); + table_cook_put(at+40+16,(uint32_t)value->icon_length,4,order); + table_cook_put(at+60,(uint64_t)value->experience,4,order); + table_cook_put(at+64,(uint64_t)value->tilt,1,order); + table_cook_put(at+66,(uint64_t)value->heading,2,order); + table_cook_put(at+72,(uint64_t)value->timestamp,8,order); + table_cook_put(at+80,(uint64_t)value->badge,1,order); + table_cook_put(at+82,(uint64_t)value->port,2,order); + table_cook_put(at+88,(uint64_t)value->epoch,8,order); + { uint64_t bits; memcpy(&bits,&value->precision,8); table_cook_put(at+96,bits,8,order); } + { int32_t cook_i1; for(cook_i1=0;cook_i1<4;cook_i1++) { + { uint32_t bits; memcpy(&bits,&value->ratings[cook_i1],4); table_cook_put(at+104+cook_i1*4,bits,4,order); } + } } + table_cook_put(at+120,value->has_loadout ? 1 : 0,1,order); + if(!schema_tabledemo_loadout_config_cook_body_(n,at+128,&value->loadout,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t profile_config_cook_measure(const ProfileConfig * value) { (void)value; return 384; } +static SCHEMA_UNUSED int profile_config_cook(const ProfileConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<384) { return 0; } + memset(raw,0,384); + if(!schema_tabledemo_profile_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+376,UINT64_C(0x19fffcb7859da778),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,304,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_root_config_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const RootConfig * value=(const RootConfig *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->version_note,value->version_note_length,17); + table_cook_put(at+0+20,(uint32_t)value->version_note_length,4,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<8;cook_i1++) { + if(!schema_tabledemo_weapon_config_cook_body_(n,at+24+cook_i1*28,&value->weapons[cook_i1],order)) { return 0; } + } } + table_cook_put(at+24+224,(uint32_t)value->weapons_count,4,order); + { int32_t cook_i2; for(cook_i2=0;cook_i2<4;cook_i2++) { + if(!schema_tabledemo_profile_config_cook_body_(n,at+256+cook_i2*304,&value->profiles[cook_i2],order)) { return 0; } + } } + table_cook_put(at+256+1216,(uint32_t)value->profiles_count,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t root_config_cook_measure(const RootConfig * value) { (void)value; return 1560; } +static SCHEMA_UNUSED int root_config_cook(const RootConfig * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<1560) { return 0; } + memset(raw,0,1560); + if(!schema_tabledemo_root_config_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+1552,UINT64_C(0x5f4843f6def45e87),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,1480,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_attachment_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Attachment * value=(const Attachment *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->slot,4,order); + { uint32_t bits; memcpy(&bits,&value->power,4); table_cook_put(at+4,bits,4,order); } + return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_buff_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Buff * value=(const Buff *)storage; + (void)n; (void)at; (void)value; (void)order; + { uint32_t bits; memcpy(&bits,&value->multiplier,4); table_cook_put(at+0,bits,4,order); } + return 1; +} +static SCHEMA_UNUSED int schema_tabledemo_debuff_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Debuff * value=(const Debuff *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->amount,4,order); + return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/examples-c/WideTable.c b/testdata/golden/tables/examples-c/WideTable.c index 87b3a3a70..3bbdf11c4 100644 --- a/testdata/golden/tables/examples-c/WideTable.c +++ b/testdata/golden/tables/examples-c/WideTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) - { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1729,9 +2330,9 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ static const TableFieldInfo schema_tabledemo_wide_blob_fields_[] = { - { "label", "label", "string", 0xe16a, 12, 0, 1, 0, 70000, (uint32_t) offsetof( WideBlob, label ), (uint32_t) sizeof( ( (WideBlob *) 0 )->label ), (uint32_t) offsetof( WideBlob, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "payload", "payload", "bytes", 0x44aa, 6, 1, 1, 0, 70000, (uint32_t) offsetof( WideBlob, payload ), (uint32_t) sizeof( ( (WideBlob *) 0 )->payload[0] ), (uint32_t) offsetof( WideBlob, payload_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "samples", "samples", "uint16", 0xaf9a, 7, 1, 1, 0, 70000, (uint32_t) offsetof( WideBlob, samples ), (uint32_t) sizeof( ( (WideBlob *) 0 )->samples[0] ), (uint32_t) offsetof( WideBlob, samples_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "label", "label", "string", 0x39f7fcec8fcb623dull, 12, 0, 1, 0, 70000, (uint32_t) offsetof( WideBlob, label ), (uint32_t) sizeof( ( (WideBlob *) 0 )->label ), (uint32_t) offsetof( WideBlob, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "payload", "payload", "bytes", 0xcfb8a9d063b5e9e5ull, 6, 1, 1, 0, 70000, (uint32_t) offsetof( WideBlob, payload ), (uint32_t) sizeof( ( (WideBlob *) 0 )->payload[0] ), (uint32_t) offsetof( WideBlob, payload_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "samples", "samples", "uint16", 0xe3b1ca6a3b48dddcull, 7, 1, 1, 0, 70000, (uint32_t) offsetof( WideBlob, samples ), (uint32_t) sizeof( ( (WideBlob *) 0 )->samples[0] ), (uint32_t) offsetof( WideBlob, samples_count ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_tabledemo_wide_blob_info_ = { "WideBlob", (uint32_t) sizeof( WideBlob ), 3, schema_tabledemo_wide_blob_fields_, schema_tabledemo_wide_blob_reset_raw_, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/examples-c/WideTable.h b/testdata/golden/tables/examples-c/WideTable.h index 98576da68..b610e2ac9 100644 --- a/testdata/golden/tables/examples-c/WideTable.h +++ b/testdata/golden/tables/examples-c/WideTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package tabledemo — protocol id 0xba7b344f28584d33 (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_TABLEDEMO_WIDETABLE_H #define SCHEMA_TABLEDEMO_WIDETABLE_H @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int counted; /* a _count/_length int32 companion exists (counted arrays, strings, bytes) */ @@ -183,6 +194,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -210,6 +223,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -231,157 +246,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[156]; + uint32_t slots[512], positions[156]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (512-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(512-1); + } + if(v->count==156) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } +} +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) +{ + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; + } + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } } - return 0; + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -391,6 +526,61 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_TABLEDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + #ifndef SCHEMA_TABLEDEMO_BUILD_VERSION #define SCHEMA_TABLEDEMO_BUILD_VERSION @@ -502,25 +692,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -530,40 +720,621 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_TABLEDEMO_TABLE_COOK */ +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + /* table WideBlob — TABLE-wire storage: relocatable, bounded. C has no member initializers, so the declared defaults live in wide_blob_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ @@ -599,78 +1370,140 @@ static SCHEMA_UNUSED int64_t wide_blob_measure( const WideBlob * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int wide_blob_save_body( TableWriter * w, const WideBlob * value ); static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int wide_blob_load_body( TableReader * r, WideBlob * value ); -static SCHEMA_UNUSED int64_t wide_blob_measure( const WideBlob * value ) +static SCHEMA_UNUSED int schema_tabledemo_wide_blob_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int wide_blob_save_message_body(TableBitWriter *,const WideBlob *); +static SCHEMA_UNUSED int wide_blob_load_message_body(TableMessageReader *,WideBlob *); +static SCHEMA_UNUSED int schema_tabledemo_wide_blob_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_tabledemo_wide_blob_wire_label_( TableWriter * w, const WideBlob * value ) +{ + if ( value->label_length < 0 || value->label_length > 70000 ) { return 0; } + table_writer_raw( w, value->label, value->label_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_wide_blob_wire_payload_( TableWriter * w, const WideBlob * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->label_length < 0 || value->label_length > 70000 ) { return -1; } /* storage invariant */ - if ( value->label_length > 0 ) { bytes += 3 + 4 + value->label_length; } /* label */ - if ( value->payload_length < 0 || value->payload_length > 70000 ) { return -1; } /* storage invariant */ - if ( value->payload_length > 0 ) { bytes += 3 + 4 + 5 + value->payload_length; } /* payload */ - if ( value->samples_count < 0 || value->samples_count > 70000 ) { return -1; } /* storage invariant */ - if ( value->samples_count > 0 ) + if ( value->payload_length < 0 || value->payload_length > 70000 ) { return 0; } + table_writer_put8( w, 6 ); table_writer_leb( w, (uint64_t) value->payload_length ); + table_writer_raw( w, value->payload, value->payload_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_tabledemo_wide_blob_wire_samples_( TableWriter * w, const WideBlob * value ) +{ + if ( value->samples_count < 0 || value->samples_count > 70000 ) { return 0; } + int32_t i; + table_writer_put8( w, 7 ); table_writer_leb( w, (uint64_t) value->samples_count ); + for ( i = 0; i < value->samples_count; i++ ) { - bytes += 3 + 4 + 5 + (int64_t) value->samples_count * 2; /* samples */ + table_writer_put16( w, (uint16_t) ( value->samples[i] ) ); } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int wide_blob_save_body( TableWriter * w, const WideBlob * value ) +static SCHEMA_UNUSED int wide_blob_save_body( TableWriter * w, const WideBlob * value ) { - if ( value->label_length < 0 || value->label_length > 70000 ) { return 0; } /* storage invariant */ - if ( value->label_length > 0 ) - { - table_writer_put16( w, 0xe16a ); table_writer_put8( w, 12 ); /* label */ - table_writer_put32( w, (uint32_t) value->label_length ); - table_writer_raw( w, value->label, value->label_length ); + (void) value; + { /* label */ + if ( value->label_length < 0 || value->label_length > 70000 ) { return 0; } + if ( value->label_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x39f7fcec8fcb623dull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_wide_blob_wire_label_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_wide_blob_wire_label_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_wide_blob_wire_label_( w, value ) ) { return 0; } + } + } } - if ( value->payload_length < 0 || value->payload_length > 70000 ) { return 0; } /* storage invariant */ - if ( value->payload_length > 0 ) - { - table_writer_put16( w, 0x44aa ); table_writer_put8( w, 14 ); /* payload */ - table_writer_put32( w, (uint32_t) ( 5 + value->payload_length ) ); - table_writer_put8( w, 6 ); table_writer_put32( w, (uint32_t) value->payload_length ); - table_writer_raw( w, value->payload, value->payload_length ); + { /* payload */ + if ( value->payload_length < 0 || value->payload_length > 70000 ) { return 0; } + if ( value->payload_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xcfb8a9d063b5e9e5ull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_wide_blob_wire_payload_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_wide_blob_wire_payload_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_wide_blob_wire_payload_( w, value ) ) { return 0; } + } + } } - if ( value->samples_count < 0 || value->samples_count > 70000 ) { return 0; } /* storage invariant */ - if ( value->samples_count > 0 ) - { - int64_t len_at_samples; - int32_t i; - table_writer_put16( w, 0xaf9a ); table_writer_put8( w, 14 ); /* samples */ - len_at_samples = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 7 ); table_writer_put32( w, (uint32_t) value->samples_count ); - for ( i = 0; i < value->samples_count; i++ ) + { /* samples */ + if ( value->samples_count < 0 || value->samples_count > 70000 ) { return 0; } + if ( value->samples_count > 0 ) { - table_writer_put16( w, (uint16_t) ( value->samples[i] ) ); + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xe3b1ca6a3b48dddcull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_tabledemo_wide_blob_wire_samples_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_tabledemo_wide_blob_wire_samples_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_tabledemo_wide_blob_wire_samples_( w, value ) ) { return 0; } + } } - table_writer_patch32( w, len_at_samples, (uint32_t) ( w->offset - len_at_samples - 4 ) ); } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t wide_blob_measure( const WideBlob * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !wide_blob_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t wide_blob_save( const WideBlob * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !wide_blob_save_body( &w, value ) ) { return -1; } - return w.offset; /* == wide_blob_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int wide_blob_load_body( TableReader * r, WideBlob * value ) +static SCHEMA_UNUSED int wide_blob_load_body( TableReader * r, WideBlob * value ) { - wide_blob_reset( value ); /* prefill declared defaults in place, then overlay */ + wide_blob_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xe16a: /* label */ + case 0x39f7fcec8fcb623dull: /* label */ { if ( kind != 12 ) { @@ -678,20 +1511,18 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int wide_blob_load_body( Tabl if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 70000 ) { keep = 70000; r->report->clamped++; } - memcpy( value->label, r->buffer + r->offset, keep ); - value->label[keep] = 0; - value->label_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->label[0] = 0; value->label_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 70000 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 70000 ); r->report->clamped++; } + memcpy( value->label, sub.buffer, (size_t) keep ); value->label[keep] = 0; value->label_length = (int32_t) keep; break; } - case 0x44aa: /* payload */ + case 0xcfb8a9d063b5e9e5ull: /* payload */ { if ( kind != 14 ) { @@ -699,43 +1530,44 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int wide_blob_load_body( Tabl if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 6 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 70000 ) { keep = 70000; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 6 ) + { + if ( !(table_kind_widens(elem_kind,6)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 70000 ) { kept = 70000; r->report->clamped++; } + value->payload_length = 0; + for ( i = 0; i < kept; i++ ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); - value->payload[i] = decoded_v; + switch ( elem_kind ) + { + case 6: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_payload; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &sub ); + value->payload[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_payload; + } + value->payload_length = (int32_t) i + 1; } - decoded = i + 1; + end_payload: ; } - value->payload_length = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0xaf9a: /* samples */ + case 0xe3b1ca6a3b48dddcull: /* samples */ { if ( kind != 14 ) { @@ -743,61 +1575,275 @@ static SCHEMA_UNUSED SCHEMA_TABLEDEMO_TABLE_INLINE int wide_blob_load_body( Tabl if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 7 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 70000 ) { keep = 70000; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 7 ) { - uint16_t decoded_v = (uint16_t) table_reader_get16( &sub ); - value->samples[i] = decoded_v; + if ( !(table_kind_widens(elem_kind,7)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; } - decoded = i + 1; + kept = count; if ( kept > 70000 ) { kept = 70000; r->report->clamped++; } + int32_t previous_count = value->samples_count; + value->samples_count = 0; + for ( i = 0; i < kept; i++ ) + { + switch ( elem_kind ) + { + case 6: + { + if ( !table_reader_has( &sub, 1 ) ) { r->report->malformed = 1; goto end_samples; } + uint64_t decoded_wide = (uint8_t) table_reader_get8( &sub ); + value->samples[i] = decoded_wide; + break; + } + case 7: + { + if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; goto end_samples; } + { + uint16_t decoded_v = (uint16_t) table_reader_get16( &sub ); + value->samples[i] = decoded_v; + } + break; + } + default: r->report->malformed = 1; goto end_samples; + } + value->samples_count = (int32_t) i + 1; + } + end_samples: ; + { uint32_t tail; for (tail=(uint32_t)value->samples_count;tail<(uint32_t)previous_count && tail<70000;tail++) { + memset(&value->samples[tail],0,sizeof(value->samples[tail])); + } } } - value->samples_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int wide_blob_load( WideBlob * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + wide_blob_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return wide_blob_load_body( &r, value ); } +static SCHEMA_UNUSED int wide_blob_save_message_body(TableBitWriter * w,const WideBlob * value) +{ + (void)value; + { /* label */ + if(value->label_length<0 || value->label_length>70000) return 0; + if(value->label_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,107,kTableMessageRefBitsHere); + if(value->label_length<0 || value->label_length>70000) return 0; + table_bit_put(w,(uint64_t)value->label_length,17); + table_bit_align_write(w); + table_bit_putbytes(w,value->label,value->label_length); + } + } + { /* payload */ + if(value->payload_length<0 || value->payload_length>70000) return 0; + if(value->payload_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,108,kTableMessageRefBitsHere); + if(value->payload_length<0 || value->payload_length>70000) return 0; + table_bit_put(w,(uint64_t)value->payload_length,17); + table_bit_align_write(w); + table_bit_putbytes(w,value->payload,value->payload_length); + } + } + { /* samples */ + if(value->samples_count<0 || value->samples_count>70000) return 0; + if(value->samples_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,109,kTableMessageRefBitsHere); + if(value->samples_count<0 || value->samples_count>70000) return 0; + table_bit_put(w,(uint64_t)value->samples_count-0,17); + { int32_t i; + for(i=0;isamples_count;i++) { + table_bit_put(w,(uint64_t)(value->samples[i])-UINT64_C(0),16); + } } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t wide_blob_measure_messages(const WideBlob * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x39f7fcec8fcb623d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,70000); + memcpy(value->label,text,(size_t)kept); + value->label[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->label_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xcfb8a9d063b5e9e5): { + if(!(entry->kind==14 && entry->elem_kind==6)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,14)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(entry->min,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + kept=n>70000 ? 70000 : n; + if(!table_bit_getbytes(&r->bits,value->payload,(int64_t)kept)||!table_message_skip_run(r,n-kept,8))goto malformed; + if(keptreport->clamped++; + value->payload_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xe3b1ca6a3b48dddc): { + if(!(entry->kind==14 && entry->elem_kind==7)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,7)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>70000?70000:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;isamples[i] : &scratch; + { uint64_t lo,hi; + if(!table_message_number(r,element_entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>65535ull){decoded=65535ull; + r->report->clamped++; + } + (*item)=(uint16_t)decoded; + } + }if(walkvalue_bits))goto malformed; + value->samples_count=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int wide_blob_load_messages(WideBlob * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define wide_blob_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define wide_blob_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define wide_blob_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define wide_blob_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define wide_blob_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* wide_blob_open: match the header and POINT. On a match the bytes ARE what this @@ -819,11 +1865,37 @@ static SCHEMA_UNUSED int wide_blob_load( WideBlob * value, const uint8_t * buffe file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const WideBlob * wide_blob_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const WideBlob *)table_cook_open_ex(bytes,length,sizeof(WideBlob),SCHEMA_TABLE_ALIGNOF(WideBlob),reason); } static SCHEMA_UNUSED const WideBlob * wide_blob_open( const void * bytes, uint64_t length ) { return (const WideBlob *) table_cook_open( bytes, length, (uint64_t) sizeof( WideBlob ), (uint64_t) SCHEMA_TABLE_ALIGNOF( WideBlob ) ); } +static SCHEMA_UNUSED int schema_tabledemo_wide_blob_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const WideBlob * value=(const WideBlob *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->label,value->label_length,70001); + table_cook_put(at+0+70004,(uint32_t)value->label_length,4,order); + table_cook_bytes(at+70008,value->payload,value->payload_length,70000); + table_cook_put(at+70008+70000,(uint32_t)value->payload_length,4,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<70000;cook_i1++) { + table_cook_put(at+140012+cook_i1*2,(uint64_t)value->samples[cook_i1],2,order); + } } + table_cook_put(at+140012+140000,(uint32_t)value->samples_count,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t wide_blob_cook_measure(const WideBlob * value) { (void)value; return 280096; } +static SCHEMA_UNUSED int wide_blob_cook(const WideBlob * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<280096) { return 0; } + memset(raw,0,280096); + if(!schema_tabledemo_wide_blob_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+280088,UINT64_C(0xab6a6961d3366451),8,order); + table_cook_header(raw,SCHEMA_TABLEDEMO_BUILD_VERSION_VALUE,280016,1,8,order); return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/golden/tables/pointers-c/GraphTable.c b/testdata/golden/tables/pointers-c/GraphTable.c index 91f5411b9..b5ba86123 100644 --- a/testdata/golden/tables/pointers-c/GraphTable.c +++ b/testdata/golden/tables/pointers-c/GraphTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else - { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1728,78 +2329,524 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ + +/* ---- json graph walk: begin ---- */ +typedef struct TableJsonGraphEntry +{ + uint64_t key; + int64_t count, label; + int open; + uint32_t node; + const TableTypeInfo * type; +} TableJsonGraphEntry; + +typedef struct TableJsonGraphMap +{ + TableJsonGraphEntry * entries; + uint64_t capacity, count; + TableAllocator allocator; +} TableJsonGraphMap; + +static SCHEMA_UNUSED uint64_t table_json_graph_slot( const TableJsonGraphMap * map, uint64_t key ) +{ + uint64_t hash = key * UINT64_C(0x9E3779B97F4A7C15); + uint64_t at; + hash ^= hash >> 29; at = hash & (map->capacity-1); + while ( map->entries[at].key != 0 && map->entries[at].key != key ) { at = (at+1) & (map->capacity-1); } + return at; +} + +static SCHEMA_UNUSED TableJsonGraphEntry * table_json_graph_find( TableJsonGraphMap * map, uint64_t key ) +{ + TableJsonGraphEntry * entry; + if ( map->capacity == 0 ) { return NULL; } + entry = &map->entries[table_json_graph_slot(map,key)]; + return entry->key == key ? entry : NULL; +} + +static SCHEMA_UNUSED TableJsonGraphEntry * table_json_graph_reach( TableJsonGraphMap * map, uint64_t key, int * taken ) +{ + TableJsonGraphEntry * entry; + if ( map->capacity == 0 || map->count >= map->capacity - map->capacity/4 - 1 ) + { + TableJsonGraphMap grown; + uint64_t i; + grown.capacity = map->capacity ? map->capacity*4 : 64; + if ( grown.capacity < map->capacity || grown.capacity > INT64_MAX / sizeof(TableJsonGraphEntry) ) { return NULL; } + grown.count = map->count; grown.allocator=map->allocator; + grown.entries = (TableJsonGraphEntry *)table_allocate(map->allocator,(int64_t)grown.capacity*sizeof(TableJsonGraphEntry)); + if ( grown.entries == NULL ) { return NULL; } + for ( i = 0; i < map->capacity; i++ ) + { + if ( map->entries[i].key ) { grown.entries[table_json_graph_slot(&grown,map->entries[i].key)] = map->entries[i]; } + } + table_release(map->allocator,map->entries); *map = grown; + } + entry = &map->entries[table_json_graph_slot(map,key)]; + *taken = entry->key != key; + if ( *taken ) { entry->key = key; map->count++; } + return entry; +} + +typedef struct TableJsonGraphIn +{ + TableWorker * worker; + TableJsonGraphMap labels; +} TableJsonGraphIn; + +static SCHEMA_UNUSED int table_json_scan_label( TableJsonIn * in, uint64_t * label ) +{ + uint64_t value = 0; + table_json_space(in); + if ( in->pos >= in->size || in->text[in->pos] < '1' || in->text[in->pos] > '9' ) { in->bad = 1; return 0; } + while ( in->pos < in->size && in->text[in->pos] >= '0' && in->text[in->pos] <= '9' ) + { + uint64_t digit = (uint64_t)(in->text[in->pos]-'0'); + if ( value > (UINT64_MAX-digit)/10 ) { in->bad = 1; return 0; } + value = value*10+digit; in->pos++; + } + *label = value; return 1; +} + +static SCHEMA_UNUSED void * table_json_emplace( TableJsonGraphIn * graph, void * slot, const TableTypeInfo * type ) +{ + uint32_t at = table_worker_bump(graph->worker,type->size); + void * node; + if ( at == kTableAllocFailed ) { return NULL; } + node = table_arena_at(graph->worker->arena,at); + type->reset(node); ((TableRef *)slot)->value = at; return node; +} + +static SCHEMA_UNUSED int table_json_read_blob( TableJsonIn * in, void * slot, const TableFieldInfo * f ) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; + TableRef * ref=(TableRef *)slot; + int64_t mark, symbols=0, length, placed=0, at; + int malformed=0, held=0; + uint32_t accumulator=0; + uint8_t * data; + if(graph==NULL || table_json_peek(in)!='"') { in->bad=1; return 0; } + ref->value=0; + if(strcmp(f->type_name,"string")==0) + { + int32_t count=0, written=0; + char * text; + mark=in->pos; + if(!table_json_scan_string(in,NULL,0,&count)) { return 0; } + in->pos=mark; text=table_string_emplace(graph->worker,ref,NULL,count); + if(text==NULL) { in->bad=1; return 0; } + return table_json_scan_string(in,text,count,&written); + } + mark=++in->pos; + for(;;) + { + char c; + if(in->pos>=in->size) { in->bad=1; return 0; } + c=in->text[in->pos++]; + if(c=='"') { break; } + if(c=='=' || malformed) { continue; } + if(table_json_base64_value((uint8_t)c)<0) { malformed=1; continue; } + symbols++; + } + if(malformed) { in->report->kind_mismatch++; return 1; } + length=symbols*6/8; + data=table_bytes_emplace(graph->worker,ref,length); + if(data==NULL) { in->bad=1; return 0; } + for(at=mark; in->text[at]!='"'; at++) + { + int32_t symbol=table_json_base64_value((uint8_t)in->text[at]); + if(symbol<0) { continue; } + accumulator=(accumulator<<6)|(uint32_t)symbol; held+=6; + if(held>=8) { held-=8; if(placed>held)&0xff); } } + } + return 1; +} + +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ + TableJsonGraphIn * graph = (TableJsonGraphIn *)in->graph; + TableJsonGraphEntry * entry; + char key[kTableJsonMaxKey], c; + int32_t length = 0; + uint64_t label = 0; + int taken = 0, bare; + void * node; + if(graph!=NULL && f->table==NULL) { return table_json_read_blob(in,slot,f); } + if ( graph == NULL || depth+1 > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; c = table_json_peek(in); + if ( c == '}' ) + { + in->pos++; + if ( table_json_emplace(graph,slot,f->table) == NULL ) { in->bad = 1; return 0; } + return 1; + } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + if ( strcmp(key,"&node") != 0 ) + { + node = table_json_emplace(graph,slot,f->table); + if ( node == NULL ) { in->bad = 1; return 0; } + return table_json_read_table_keys(in,node,f->table,depth+1,key); + } + if ( !table_json_scan_label(in,&label) ) { return 0; } + entry = table_json_graph_reach(&graph->labels,label,&taken); + if ( entry == NULL ) { in->bad = 1; return 0; } + c = table_json_peek(in); + if ( c == ',' ) { in->pos++; c = table_json_peek(in); } + bare = c == '}'; + if ( bare == taken ) { in->bad = 1; return 0; } + if ( bare ) + { + in->pos++; + if ( entry->open ) { in->bad = 1; return 0; } + ((TableRef *)slot)->value = 0; + if ( entry->type == NULL ) { return 1; } + if ( entry->type != f->table ) { in->report->kind_mismatch++; return 1; } + ((TableRef *)slot)->value = entry->node; return 1; + } + node = table_json_emplace(graph,slot,f->table); + if ( node == NULL ) { in->bad = 1; return 0; } + entry->open = 1; + if ( !table_json_read_table_keys(in,node,f->table,depth+1,NULL) ) { return 0; } + entry = table_json_graph_find(&graph->labels,label); + if ( entry == NULL ) { in->bad = 1; return 0; } + entry->node = (uint32_t)((TableRef *)slot)->value; entry->type = f->table; entry->open = 0; return 1; +} + +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ + TableJsonGraphIn * graph = (TableJsonGraphIn *)in->graph; + uint64_t label = 0; + int taken = 0; + if ( graph == NULL || strcmp(key,"&node") != 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_label(in,&label) ) { return 0; } + if ( table_json_graph_reach(&graph->labels,label,&taken) == NULL ) { in->bad = 1; return 0; } + return 1; +} + +typedef struct TableJsonGraphOut +{ + TableJsonGraphMap nodes; + int counting; + int64_t next_label; +} TableJsonGraphOut; + +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ + TableJsonGraphOut * graph = (TableJsonGraphOut *)out->graph; + const void * node = table_ref_at(NULL,(const TableRef *)slot); + TableJsonGraphEntry * entry; + int taken = 0, any = 1; + int64_t before; + if ( graph == NULL ) { return 0; } + if ( node == NULL ) { table_json_raw(out,"null",4); return 1; } + entry = table_json_graph_reach(&graph->nodes,(uint64_t)(uintptr_t)node,&taken); + if ( entry == NULL ) { return 0; } + if(f->table==NULL) + { + const TableBlob * blob=(const TableBlob *)node; + if(graph->counting) { entry->count++; return 1; } + if(entry->count>1 || blob->length>INT32_MAX) { return 0; } + if(strcmp(f->type_name,"string")==0) { table_json_write_string(out,(const char *)(blob+1),(int32_t)blob->length); } + else { table_json_write_base64(out,(const uint8_t *)(blob+1),(int32_t)blob->length); } + return 1; + } + if ( graph->counting ) + { + entry->count++; + if ( !taken ) { return entry->open == 0; } + entry->open = 1; + if ( !table_json_write_value(out,node,f->table,depth) ) { return 0; } + entry = table_json_graph_find(&graph->nodes,(uint64_t)(uintptr_t)node); + if ( entry == NULL ) { return 0; } + entry->open = 0; return 1; + } + if ( entry->count <= 1 ) { return table_json_write_value(out,node,f->table,depth); } + if ( depth > kTableJsonMaxDepth ) { return 0; } + table_json_put(out,'{'); table_json_line(out,depth+1); table_json_raw(out,"\"&node\": ",9); + if ( entry->label ) + { + table_json_write_unsigned(out,(uint64_t)entry->label); + table_json_line(out,depth); table_json_put(out,'}'); return 1; + } + entry->label = ++graph->next_label; + table_json_write_unsigned(out,(uint64_t)entry->label); + before = out->offset; + if ( !table_json_write_fields(out,node,f->table,depth,&any) || out->offset == before ) { return 0; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + +static SCHEMA_UNUSED int table_json_read_graph( TableWorker * worker, void * root, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) +{ + TableJsonGraphIn graph; + TableJsonIn in; + TableReport ignored = {0}; + int ok; + memset(&graph,0,sizeof(graph)); graph.worker = worker; graph.labels.allocator=worker->arena!=NULL ? worker->arena->allocator : table_default_allocator(); + memset(&in,0,sizeof(in)); in.text = text; in.size = bytes; in.report = report ? report : &ignored; in.graph = &graph; + if ( root == NULL || worker->arena == NULL || text == NULL || bytes < 0 ) { in.report->malformed = 1; return 0; } + info->reset(root); + ok = table_json_read_table(&in,root,info,0); + if ( ok ) { table_json_space(&in); if ( in.pos != in.size ) { in.bad = 1; } } + table_release(graph.labels.allocator,graph.labels.entries); + if ( in.bad || !ok ) { in.report->malformed = 1; return 0; } + return 1; +} + +static SCHEMA_UNUSED int64_t table_json_write_graph( const void * root, const TableTypeInfo * info, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + TableJsonGraphOut graph; + TableJsonOut count, out; + TableJsonGraphEntry * entry; + int taken = 0, ok; + if ( root == NULL ) { return -1; } + memset(&graph,0,sizeof(graph)); graph.counting = 1; graph.nodes.allocator=allocator; + entry = table_json_graph_reach(&graph.nodes,(uint64_t)(uintptr_t)root,&taken); + if ( entry == NULL ) { return -1; } + entry->open = 1; + memset(&count,0,sizeof(count)); count.graph = &graph; + ok = table_json_write_value(&count,root,info,0); + graph.counting = 0; + memset(&out,0,sizeof(out)); out.buffer = buffer; out.capacity = capacity; out.graph = &graph; + if ( ok ) { ok = table_json_write_value(&out,root,info,0); } + table_release(graph.nodes.allocator,graph.nodes.entries); + if ( !ok ) { return -1; } + table_json_put(&out,'\n'); return out.overflow ? -1 : out.offset; +} +/* ---- json graph walk: end ---- */ + +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth) +{ + const TableSequence * list=(const TableSequence *)storage; + TableSequenceCursor cursor=table_sequence_cursor(NULL,list,f->elem_size); int32_t i; + if(!cursor.ok) { return 0; } + table_json_put(out,'['); + for(i=0;icount;i++) { + const void * element=table_sequence_next(&cursor); if(element==NULL) { return 0; } + if(i) { table_json_put(out,','); } table_json_line(out,depth+1); + if(!table_json_write_scalar(out,element,f,depth+1)) { return 0; } + } + if(list->count) { table_json_line(out,depth); } table_json_put(out,']'); return 1; +} +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; + TableSequence * list=(TableSequence *)storage; char shape=table_json_element_shape(f); + if(graph==NULL || table_json_peek(in)!='[') { in->bad=1; return 0; } + memset(list,0,sizeof(*list)); in->pos++; + for(;;) { + char c=table_json_peek(in); void * element; + if(c==']') { in->pos++; return 1; } + if(c==0) { in->bad=1; return 0; } + element=table_sequence_add(graph->worker,list,f->elem_size); + if(element==NULL) { in->bad=1; return 0; } + if(f->kind==13) { f->table->reset(element); } + if(table_json_value_shape(in)!=shape && !(f->kind==17 && table_json_value_shape(in)=='z')) { + in->report->kind_mismatch++; if(!table_json_skip_value(in,depth+1)) { return 0; } + } else if(!table_json_read_scalar(in,element,f,depth+1)) { return 0; } + c=table_json_peek(in); + if(c==',') { in->pos++; continue; } + if(c==']') { in->pos++; return 1; } + in->bad=1; return 0; + } +} +#endif + +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED TableJsonInteger table_json_interpret_exact( const char * token, int32_t length ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + int32_t i = 0; + if ( i < length && token[i] == '-' ) { out.negative = 1; i++; } // WalkNumber refuses a leading plus + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // leading and trailing zeros stripped, so the last digit kept is significant + int32_t start = 0, end = int_len + frac_len; + int64_t point = (int64_t) int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + const int64_t digits = end - start; + if ( digits == 0 ) { out.negative = 0; return out; } // the value is zero, and -0 IS zero + if ( point < digits ) { out.fractional = 1; return out; } // a significant digit below the point + if ( point > 20 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + for ( int32_t k = start; k < end; k++ ) + { + const uint64_t digit = (uint64_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + out.magnitude = out.magnitude * 10 + digit; + } + for ( int64_t k = digits; k < point; k++ ) // the point's own zeros, which no digit spells + { + if ( out.magnitude > UINT64_MAX / 10 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + out.magnitude *= 10; + } + return out; +} + +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth) +{ + const TableSequence * map=(const TableSequence *)storage; + TableSequenceCursor cursor=table_sequence_cursor(NULL,map,f->elem_size); int32_t i; + const TableFieldInfo * key=&f->table->fields[0]; const TableFieldInfo * value=&f->table->fields[1]; + if(!cursor.ok) { return 0; } table_json_put(out,'{'); + for(i=0;icount;i++) { + const uint8_t * entry=(const uint8_t *)table_sequence_next(&cursor); if(entry==NULL) { return 0; } + if(i) { table_json_put(out,','); } table_json_line(out,depth+1); + if(key->kind==12) { table_json_write_string(out,(const char *)entry+key->offset,table_json_count(entry,key)); } + else { + table_json_put(out,'"'); + if(key->kind>=2 && key->kind<=5) { table_json_write_signed(out,table_json_get_signed(entry+key->offset,key->elem_size)); } + else { table_json_write_unsigned(out,table_json_get_raw(entry+key->offset,key->elem_size)); } + table_json_put(out,'"'); + } + table_json_raw(out,": ",2); if(!table_json_write_field(out,entry,value,depth+1)) { return 0; } + } + if(map->count) { table_json_line(out,depth); } table_json_put(out,'}'); return 1; +} +static SCHEMA_UNUSED int table_json_map_key_value(const char * token,int32_t length,const TableFieldInfo * key,int64_t * value,int * fits) +{ + TableReport scratch={0}; TableJsonIn probe; TableJsonInteger number; int integral=0,moved=0; + memset(&probe,0,sizeof(probe)); probe.text=token; probe.size=length; probe.report=&scratch; *fits=0; + table_json_space(&probe); if(probe.pos!=0 || !table_json_walk_number(&probe,&integral) || probe.pos!=length) { return 0; } + number=table_json_interpret_exact(token,length); if(number.fractional || number.saturated) { return 1; } + *value=table_json_integer_in_domain(number,key->kind>=2 && key->kind<=5,(int32_t)key->elem_size,&moved); *fits=!moved; return 1; +} +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; TableSequence * map=(TableSequence *)storage; + const TableFieldInfo * key=&f->table->fields[0]; const TableFieldInfo * value=&f->table->fields[1]; char shape=table_json_shape(value); + if(graph==NULL || table_json_peek(in)!='{' || depth+1>kTableJsonMaxDepth) { in->bad=1; return 0; } + memset(map,0,sizeof(*map)); in->pos++; + for(;;) { + char c=table_json_peek(in),token[kTableJsonMaxKey]; int32_t length=0,before; int64_t number=0; int place=1; void * entry; + TableReport scratch={0}; TableJsonIn scan=*in; scan.report=&scratch; + if(c=='}') { in->pos++; return 1; } if(c==0) { in->bad=1; return 0; } + if(!table_json_scan_string(&scan,token,kTableJsonMaxKey-1,&length)) { in->pos=scan.pos; in->bad=1; return 0; } + in->pos=scan.pos; token[length]=0; + if(table_json_peek(in)!=':') { in->bad=1; return 0; } in->pos++; + if(key->kind==12) { + if(scratch.clamped || length>key->array_bound) { in->report->clamped++; place=0; } + } else { + int fits=0; + if(scratch.clamped) { in->report->kind_mismatch++; place=0; } + else if(!table_json_map_key_value(token,length,key,&number,&fits)) { in->bad=1; return 0; } + else if(!fits) { in->report->kind_mismatch++; place=0; } + } + before=map->count; entry=place ? f->place(graph->worker,map,token,length,number) : NULL; + if(place && entry==NULL) { in->bad=1; return 0; } + if(entry!=NULL && map->count==before) { in->report->duplicate++; } + c=table_json_value_shape(in); + if(entry==NULL) { if(!table_json_skip_value(in,depth+1)) { return 0; } } + else if(c!=shape && !(value->kind==17 && !value->is_array && c=='z')) { + in->report->kind_mismatch++; if(!table_json_skip_value(in,depth+1)) { return 0; } + } else if(!table_json_read_field(in,entry,value,depth+1)) { return 0; } + c=table_json_peek(in); if(c==',') { in->pos++; continue; } if(c=='}') { in->pos++; return 1; } + in->bad=1; return 0; + } +} +#endif static const TableFieldInfo schema_graphdemo_meta_fields_[] = { - { "build", "build", "int32", 0x3138, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Meta, build ), (uint32_t) sizeof( ( (Meta *) 0 )->build ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "tag", "tag", "string", 0xbc64, 12, 0, 0, 1, 0, 8, (uint32_t) offsetof( Meta, tag ), (uint32_t) sizeof( ( (Meta *) 0 )->tag ), (uint32_t) offsetof( Meta, tag_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "build", "build", "int32", 0x802517e298c70b03ull, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Meta, build ), (uint32_t) sizeof( ( (Meta *) 0 )->build ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tag", "tag", "string", 0x56d7ab194448a4f3ull, 12, 0, 0, 1, 0, 8, (uint32_t) offsetof( Meta, tag ), (uint32_t) sizeof( ( (Meta *) 0 )->tag ), (uint32_t) offsetof( Meta, tag_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_meta_info_ = { "Meta", (uint32_t) sizeof( Meta ), 2, schema_graphdemo_meta_fields_, schema_graphdemo_meta_reset_raw_, 0, TableDocNone, 0, NULL }; static const TableFieldInfo schema_graphdemo_settings_fields_[] = { - { "quality", "quality", "int32", 0xcaf3, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Settings, quality ), (uint32_t) sizeof( ( (Settings *) 0 )->quality ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "label", "label", "string", 0xe16a, 12, 0, 0, 1, 0, 16, (uint32_t) offsetof( Settings, label ), (uint32_t) sizeof( ( (Settings *) 0 )->label ), (uint32_t) offsetof( Settings, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "quality", "quality", "int32", 0x7a8060916400fe66ull, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Settings, quality ), (uint32_t) sizeof( ( (Settings *) 0 )->quality ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 4.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "label", "label", "string", 0x39f7fcec8fcb623dull, 12, 0, 0, 1, 0, 16, (uint32_t) offsetof( Settings, label ), (uint32_t) sizeof( ( (Settings *) 0 )->label ), (uint32_t) offsetof( Settings, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_settings_info_ = { "Settings", (uint32_t) sizeof( Settings ), 2, schema_graphdemo_settings_fields_, schema_graphdemo_settings_reset_raw_, 0, TableDocNone, 0, NULL }; static const TableFieldInfo schema_graphdemo_list_node_fields_[] = { - { "value", "value", "int32", 0x9194, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( ListNode, value ), (uint32_t) sizeof( ( (ListNode *) 0 )->value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "name", "name", "string", 0x30df, 12, 0, 0, 1, 0, 12, (uint32_t) offsetof( ListNode, name ), (uint32_t) sizeof( ( (ListNode *) 0 )->name ), (uint32_t) offsetof( ListNode, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "next", "next", "ListNode", 0xd15e, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( ListNode, next ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "value", "value", "int32", 0x7ce4fd9430e80ceaull, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( ListNode, value ), (uint32_t) sizeof( ( (ListNode *) 0 )->value ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "name", "name", "string", 0xc4bcadba8e631b86ull, 12, 0, 0, 1, 0, 12, (uint32_t) offsetof( ListNode, name ), (uint32_t) sizeof( ( (ListNode *) 0 )->name ), (uint32_t) offsetof( ListNode, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "next", "next", "ListNode", 0xe5316cbaa025f028ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( ListNode, next ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_list_node_info_ = { "ListNode", (uint32_t) sizeof( ListNode ), 3, schema_graphdemo_list_node_fields_, schema_graphdemo_list_node_reset_raw_, 1, TableDocNone, 0, NULL }; static const TableFieldInfo schema_graphdemo_tree_node_fields_[] = { - { "label", "label", "string", 0xe16a, 12, 0, 0, 1, 0, 12, (uint32_t) offsetof( TreeNode, label ), (uint32_t) sizeof( ( (TreeNode *) 0 )->label ), (uint32_t) offsetof( TreeNode, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "left", "left", "TreeNode", 0xfe3a, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( TreeNode, left ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_tree_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "right", "right", "TreeNode", 0x5506, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( TreeNode, right ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_tree_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "label", "label", "string", 0x39f7fcec8fcb623dull, 12, 0, 0, 1, 0, 12, (uint32_t) offsetof( TreeNode, label ), (uint32_t) sizeof( ( (TreeNode *) 0 )->label ), (uint32_t) offsetof( TreeNode, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "left", "left", "TreeNode", 0x24b070ada2041cb0ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( TreeNode, left ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_tree_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "right", "right", "TreeNode", 0x76aaaa535714d805ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( TreeNode, right ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_tree_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_tree_node_info_ = { "TreeNode", (uint32_t) sizeof( TreeNode ), 3, schema_graphdemo_tree_node_fields_, schema_graphdemo_tree_node_reset_raw_, 1, TableDocNone, 0, NULL }; static const TableFieldInfo schema_graphdemo_layer_fields_[] = { - { "depth", "depth", "int32", 0x609f, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Layer, depth ), (uint32_t) sizeof( ( (Layer *) 0 )->depth ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 64.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "head", "head", "ListNode", 0x79aa, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Layer, head ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "depth", "depth", "int32", 0x75d8e97600b296eaull, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Layer, depth ), (uint32_t) sizeof( ( (Layer *) 0 )->depth ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 64.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "head", "head", "ListNode", 0x0a8f12cc5f9a0c03ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Layer, head ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_layer_info_ = { "Layer", (uint32_t) sizeof( Layer ), 2, schema_graphdemo_layer_fields_, schema_graphdemo_layer_reset_raw_, 1, TableDocNone, 0, NULL }; static const TableFieldInfo schema_graphdemo_scene_fields_[] = { - { "name", "name", "string", 0x30df, 12, 0, 0, 1, 0, 24, (uint32_t) offsetof( Scene, name ), (uint32_t) sizeof( ( (Scene *) 0 )->name ), (uint32_t) offsetof( Scene, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "version", "version", "int32", 0xe8e6, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Scene, version ), (uint32_t) sizeof( ( (Scene *) 0 )->version ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 99.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "head", "head", "ListNode", 0x79aa, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Scene, head ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "tree", "tree", "TreeNode", 0x595e, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Scene, tree ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_tree_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "settings", "settings", "Settings", 0x130e, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Scene, settings ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_settings_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "alias", "alias", "ListNode", 0xfc71, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Scene, alias ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "ground", "ground", "Layer", 0x5bdf, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Scene, ground ), (uint32_t) sizeof( ( (Scene *) 0 )->ground ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_layer_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "layers", "layers", "Layer", 0x1ee8, 13, 1, 0, 1, 0, 4, (uint32_t) offsetof( Scene, layers ), (uint32_t) sizeof( ( (Scene *) 0 )->layers[0] ), (uint32_t) offsetof( Scene, layers_count ), 0xffffffffu, &schema_graphdemo_layer_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "meta", "meta", "Meta", 0xcea6, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Scene, meta ), (uint32_t) sizeof( ( (Scene *) 0 )->meta ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_meta_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "name", "name", "string", 0xc4bcadba8e631b86ull, 12, 0, 0, 1, 0, 24, (uint32_t) offsetof( Scene, name ), (uint32_t) sizeof( ( (Scene *) 0 )->name ), (uint32_t) offsetof( Scene, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "version", "version", "int32", 0xbb62c62c9808ea37ull, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Scene, version ), (uint32_t) sizeof( ( (Scene *) 0 )->version ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 99.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "head", "head", "ListNode", 0x0a8f12cc5f9a0c03ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Scene, head ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tree", "tree", "TreeNode", 0x5b25b8ef511eb395ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Scene, tree ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_tree_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "settings", "settings", "Settings", 0xee5f6d7b48b44de8ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Scene, settings ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_settings_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "alias", "alias", "ListNode", 0x509220bb65a646b7ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Scene, alias ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "ground", "ground", "Layer", 0x60839e2395be697eull, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Scene, ground ), (uint32_t) sizeof( ( (Scene *) 0 )->ground ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_layer_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "layers", "layers", "Layer", 0x4554e34a747022dfull, 13, 1, 0, 1, 0, 4, (uint32_t) offsetof( Scene, layers ), (uint32_t) sizeof( ( (Scene *) 0 )->layers[0] ), (uint32_t) offsetof( Scene, layers_count ), 0xffffffffu, &schema_graphdemo_layer_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "meta", "meta", "Meta", 0x4320e9a2e32eac38ull, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Scene, meta ), (uint32_t) sizeof( ( (Scene *) 0 )->meta ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_meta_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_scene_info_ = { "Scene", (uint32_t) sizeof( Scene ), 9, schema_graphdemo_scene_fields_, schema_graphdemo_scene_reset_raw_, 1, TableDocNone, 0, NULL }; static const TableVariantInfo schema_graphdemo_depot_banks_keys_[] = { - { "None", 0x0000 }, - { "Low", 0x24fc }, - { "High", 0xb817 }, + { "None", 0x0000000000000000ull }, + { "Low", 0x24f3a319b88552c1ull }, + { "High", 0x9deeefd89ca8a81dull }, }; static const TableFieldInfo schema_graphdemo_depot_fields_[] = { - { "name", "name", "string", 0x30df, 12, 0, 0, 1, 0, 12, (uint32_t) offsetof( Depot, name ), (uint32_t) sizeof( ( (Depot *) 0 )->name ), (uint32_t) offsetof( Depot, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "banks", "banks", "Layer", 0x7f34, 13, 1, 0, 0, 0, (int32_t) TIER_MAX, (uint32_t) offsetof( Depot, banks ), (uint32_t) sizeof( ( (Depot *) 0 )->banks[0] ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_layer_info_, 0, 0.0, 0.0, -1, NULL, 0, "Tier", schema_graphdemo_depot_banks_keys_, 2, NULL, "", TableDocNone, 0, NULL }, - { "spare", "spare", "Meta", 0x3a4f, 13, 0, 0, 0, 1, 0, (uint32_t) offsetof( Depot, spare ), (uint32_t) sizeof( ( (Depot *) 0 )->spare ), 0xffffffffu, (uint32_t) offsetof( Depot, spare_present ), &schema_graphdemo_meta_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "head", "head", "ListNode", 0x79aa, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Depot, head ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "name", "name", "string", 0xc4bcadba8e631b86ull, 12, 0, 0, 1, 0, 12, (uint32_t) offsetof( Depot, name ), (uint32_t) sizeof( ( (Depot *) 0 )->name ), (uint32_t) offsetof( Depot, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "banks", "banks", "Layer", 0xdd9eb981e152e90cull, 13, 1, 0, 0, 0, (int32_t) TIER_MAX, (uint32_t) offsetof( Depot, banks ), (uint32_t) sizeof( ( (Depot *) 0 )->banks[0] ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_layer_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, "Tier", schema_graphdemo_depot_banks_keys_, 2, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "spare", "spare", "Meta", 0x4339ee8ab21c8380ull, 13, 0, 0, 0, 1, 0, (uint32_t) offsetof( Depot, spare ), (uint32_t) sizeof( ( (Depot *) 0 )->spare ), 0xffffffffu, (uint32_t) offsetof( Depot, spare_present ), &schema_graphdemo_meta_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "head", "head", "ListNode", 0x0a8f12cc5f9a0c03ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Depot, head ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_depot_info_ = { "Depot", (uint32_t) sizeof( Depot ), 4, schema_graphdemo_depot_fields_, schema_graphdemo_depot_reset_raw_, 1, TableDocNone, 0, NULL }; static const TableFieldInfo schema_graphdemo_album_fields_[] = { - { "name", "name", "string", 0x30df, 12, 0, 0, 1, 0, 16, (uint32_t) offsetof( Album, name ), (uint32_t) sizeof( ( (Album *) 0 )->name ), (uint32_t) offsetof( Album, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "tint", "tint", "Colour", 0x82b9, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Album, tint ), (uint32_t) sizeof( ( (Album *) 0 )->tint ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_colour_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "stamp", "stamp", "Stamp", 0x0dc6, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Album, stamp ), (uint32_t) sizeof( ( (Album *) 0 )->stamp ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_stamp_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "marker", "marker", "Marker", 0x866f, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Album, marker ), (uint32_t) sizeof( ( (Album *) 0 )->marker ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_marker_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "pin", "pin", "Marker", 0x69d5, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Album, pin ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_marker_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "head", "head", "ListNode", 0x79aa, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Album, head ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "name", "name", "string", 0xc4bcadba8e631b86ull, 12, 0, 0, 1, 0, 16, (uint32_t) offsetof( Album, name ), (uint32_t) sizeof( ( (Album *) 0 )->name ), (uint32_t) offsetof( Album, name_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "tint", "tint", "Colour", 0x1e4984ef2e958a4cull, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Album, tint ), (uint32_t) sizeof( ( (Album *) 0 )->tint ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_colour_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "stamp", "stamp", "Stamp", 0xee7ba9ad45c64144ull, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Album, stamp ), (uint32_t) sizeof( ( (Album *) 0 )->stamp ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_stamp_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "marker", "marker", "Marker", 0xeddcb72b15486e77ull, 13, 0, 0, 0, 0, 0, (uint32_t) offsetof( Album, marker ), (uint32_t) sizeof( ( (Album *) 0 )->marker ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_marker_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "pin", "pin", "Marker", 0x77af761956600b54ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Album, pin ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_marker_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "head", "head", "ListNode", 0x0a8f12cc5f9a0c03ull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Album, head ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_list_node_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_album_info_ = { "Album", (uint32_t) sizeof( Album ), 6, schema_graphdemo_album_fields_, schema_graphdemo_album_reset_raw_, 1, TableDocNone, 0, NULL }; @@ -1824,3 +2871,63 @@ int64_t schema_graphdemo_settings_to_json_( const Settings * value, char * buffe return table_json_write( value, &schema_graphdemo_settings_info_, buffer, capacity ); } +int schema_graphdemo_list_node_from_json_( ListNodeBuilder * builder, const char * text, int64_t bytes, TableReport * report ) +{ + return table_json_read_graph(&builder->main,list_node_builder_root(builder),&schema_graphdemo_list_node_info_,text,bytes,report); +} + +int64_t schema_graphdemo_list_node_to_json_( const ListNode * value, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + return table_json_write_graph(value,&schema_graphdemo_list_node_info_,buffer,capacity,allocator); +} + +int schema_graphdemo_tree_node_from_json_( TreeNodeBuilder * builder, const char * text, int64_t bytes, TableReport * report ) +{ + return table_json_read_graph(&builder->main,tree_node_builder_root(builder),&schema_graphdemo_tree_node_info_,text,bytes,report); +} + +int64_t schema_graphdemo_tree_node_to_json_( const TreeNode * value, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + return table_json_write_graph(value,&schema_graphdemo_tree_node_info_,buffer,capacity,allocator); +} + +int schema_graphdemo_layer_from_json_( LayerBuilder * builder, const char * text, int64_t bytes, TableReport * report ) +{ + return table_json_read_graph(&builder->main,layer_builder_root(builder),&schema_graphdemo_layer_info_,text,bytes,report); +} + +int64_t schema_graphdemo_layer_to_json_( const Layer * value, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + return table_json_write_graph(value,&schema_graphdemo_layer_info_,buffer,capacity,allocator); +} + +int schema_graphdemo_scene_from_json_( SceneBuilder * builder, const char * text, int64_t bytes, TableReport * report ) +{ + return table_json_read_graph(&builder->main,scene_builder_root(builder),&schema_graphdemo_scene_info_,text,bytes,report); +} + +int64_t schema_graphdemo_scene_to_json_( const Scene * value, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + return table_json_write_graph(value,&schema_graphdemo_scene_info_,buffer,capacity,allocator); +} + +int schema_graphdemo_depot_from_json_( DepotBuilder * builder, const char * text, int64_t bytes, TableReport * report ) +{ + return table_json_read_graph(&builder->main,depot_builder_root(builder),&schema_graphdemo_depot_info_,text,bytes,report); +} + +int64_t schema_graphdemo_depot_to_json_( const Depot * value, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + return table_json_write_graph(value,&schema_graphdemo_depot_info_,buffer,capacity,allocator); +} + +int schema_graphdemo_album_from_json_( AlbumBuilder * builder, const char * text, int64_t bytes, TableReport * report ) +{ + return table_json_read_graph(&builder->main,album_builder_root(builder),&schema_graphdemo_album_info_,text,bytes,report); +} + +int64_t schema_graphdemo_album_to_json_( const Album * value, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + return table_json_write_graph(value,&schema_graphdemo_album_info_,buffer,capacity,allocator); +} + diff --git a/testdata/golden/tables/pointers-c/GraphTable.h b/testdata/golden/tables/pointers-c/GraphTable.h index a02e3f567..e638877f6 100644 --- a/testdata/golden/tables/pointers-c/GraphTable.h +++ b/testdata/golden/tables/pointers-c/GraphTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package graphdemo — protocol id 0x36ff4b4cd88bf37a (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_GRAPHDEMO_GRAPHTABLE_H #define SCHEMA_GRAPHDEMO_GRAPHTABLE_H @@ -13,8 +13,8 @@ #include #include /* offsetof, for the reflection descriptors */ #include /* memcpy, memset — the prefill and the wire's raw moves */ -#include /* the arena's segments (the AUTHORING path may allocate) */ #include /* the keyed accessor's None refusal, in a debug build */ +#include /* and its abort, which NDEBUG does not remove */ #include "Graph.h" #include "MarksTable.h" @@ -111,8 +111,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -121,6 +127,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -130,6 +137,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -156,7 +165,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -166,12 +175,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int is_pointer; /* a *T pointer field: storage is an 8-byte TableRef; the target is a table */ @@ -186,6 +197,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -213,6 +226,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -239,157 +254,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[48]; + uint32_t slots[128], positions[48]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + struct TableNumbering * nodes; } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (128-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(128-1); + } + if(v->count==48) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + struct TableNodeMap * nodes; + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; } - return 0; + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -399,6 +534,94 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_GRAPHDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + + +#ifndef SCHEMA_TABLE_ALLOCATOR_RUNTIME +#define SCHEMA_TABLE_ALLOCATOR_RUNTIME +#ifndef schema_allocate +#include +#define schema_allocate(bytes) calloc(1,(size_t)(bytes)) +#endif +#ifndef schema_release +#include +#define schema_release(pointer) free(pointer) +#endif +typedef struct TableAllocator +{ + void * (*alloc)(void * context,int64_t bytes); + void (*free)(void * context,void * pointer); + void * context; +} TableAllocator; +static SCHEMA_UNUSED void * table_default_allocate(void * context,int64_t bytes) +{ (void)context; return bytes>0 && (uint64_t)bytes<=SIZE_MAX ? schema_allocate(bytes) : NULL; } +static SCHEMA_UNUSED void table_default_release(void * context,void * pointer) +{ (void)context; schema_release(pointer); } +static SCHEMA_UNUSED TableAllocator table_default_allocator(void) +{ + TableAllocator allocator={table_default_allocate,table_default_release,NULL}; return allocator; +} +static SCHEMA_UNUSED void * table_allocate(TableAllocator allocator,int64_t bytes) +{ + if(allocator.alloc==NULL || allocator.free==NULL || bytes<=0 || (uint64_t)bytes>SIZE_MAX) { return NULL; } + return allocator.alloc(allocator.context,bytes); +} +static SCHEMA_UNUSED void table_release(TableAllocator allocator,void * pointer) +{ if(allocator.free!=NULL) { allocator.free(allocator.context,pointer); } } +#endif #ifndef SCHEMA_GRAPHDEMO_TABLE_ARENA #define SCHEMA_GRAPHDEMO_TABLE_ARENA @@ -565,10 +788,11 @@ typedef struct TableArena { TableAtomicPtr segments[ kTableMaxSegments ]; TableAtomicU32 cursor; /* (segment << kTableSegmentBits) | bytes handed out */ + TableAllocator allocator; int locked; /* MONOTONIC: Lock is one-way, there is no unlock */ } TableArena; -static SCHEMA_UNUSED void table_arena_init( TableArena * arena ) +static SCHEMA_UNUSED void table_arena_init_with_allocator( TableArena * arena, TableAllocator allocator ) { uint32_t i; for ( i = 0; i < kTableMaxSegments; i++ ) @@ -576,9 +800,12 @@ static SCHEMA_UNUSED void table_arena_init( TableArena * arena ) table_atomic_store_ptr( &arena->segments[i], (uint8_t *) NULL ); } table_atomic_store32( &arena->cursor, 0 ); - arena->locked = 0; + arena->locked = 0; arena->allocator=allocator; } +static SCHEMA_UNUSED void table_arena_init(TableArena * arena) +{ table_arena_init_with_allocator(arena,table_default_allocator()); } + static SCHEMA_UNUSED void table_arena_shutdown( TableArena * arena ) { uint32_t i; @@ -588,7 +815,7 @@ static SCHEMA_UNUSED void table_arena_shutdown( TableArena * arena ) if ( segment != NULL ) { uint8_t * expected = segment; - if ( table_arena_cas_ptr( &arena->segments[i], &expected, (uint8_t *) NULL ) ) { free( segment ); } + if ( table_arena_cas_ptr( &arena->segments[i], &expected, (uint8_t *) NULL ) ) { table_release(arena->allocator,segment); } } } table_atomic_store32( &arena->cursor, 0 ); @@ -623,12 +850,12 @@ static SCHEMA_UNUSED uint32_t table_arena_grab_slab( TableArena * arena ) region. It costs nothing measurable: a fresh segment is untouched pages either way, and calloc has the kernel hand them over zeroed. */ - uint8_t * memory = (uint8_t *) calloc( 1, kTableSegmentSize ); + uint8_t * memory = (uint8_t *) table_allocate(arena->allocator,kTableSegmentSize); uint8_t * expected = NULL; if ( memory == NULL ) { return kTableAllocFailed; } if ( !table_arena_cas_ptr( &arena->segments[segment], &expected, memory ) ) { - free( memory ); /* another worker published this segment first */ + table_release(arena->allocator,memory); /* another worker published this segment first */ } } if ( table_arena_cas32( &arena->cursor, &cursor, cursor + kTableSlabBytes ) ) @@ -674,24 +901,44 @@ static SCHEMA_UNUSED TableWorker table_worker_make( TableArena * arena ) arena offset, or kTableAllocFailed. It is the untyped half of every generated _emplace: the type's own size and its reset stay in the generated code, where they can be spelled. */ -static SCHEMA_UNUSED uint32_t table_worker_bump( TableWorker * worker, uint32_t bytes ) +static SCHEMA_UNUSED uint32_t table_arena_grab_span( TableArena * arena, int64_t bytes ) +{ + uint32_t spanned; + if(bytes<=0 || bytes>((int64_t)kTableMaxSegments-2)*kTableSegmentSize) { return kTableAllocFailed; } + spanned=(uint32_t)((bytes+kTableSegmentSize-1)>>kTableSegmentBits); + for(;;) + { + uint32_t cursor=table_atomic_load32(&arena->cursor); + uint32_t start=(cursor>>kTableSegmentBits)+1; + uint32_t next; + uint8_t * memory; + if(start+spanned>=kTableMaxSegments) { return kTableAllocFailed; } + next=(start+spanned)<cursor,&cursor,next)) { continue; } + memory=(uint8_t *)table_allocate(arena->allocator,bytes); + if(memory==NULL) { return kTableAllocFailed; } + table_atomic_store_ptr(&arena->segments[start],memory); + return start<arena == NULL || worker->arena->locked ) { return kTableAllocFailed; } - bytes = table_align_up( bytes ); - if ( bytes > kTableSlabBytes ) { return kTableAllocFailed; } /* a node larger than a slab: refused, never split */ - if ( worker->end == 0 || worker->next + bytes > worker->end ) + if(worker->arena==NULL || worker->arena->locked || bytes<=0 || bytes>INT64_MAX-kTableAlign) { return kTableAllocFailed; } + bytes=table_align_up64(bytes); + if(bytes>kTableSlabBytes) { return table_arena_grab_span(worker->arena,bytes); } + if(worker->end==0 || (uint64_t)worker->next+(uint64_t)bytes>worker->end || (worker->next==0 && bytes+kTableAlign>kTableSlabBytes)) { - uint32_t offset = table_arena_grab_slab( worker->arena ); - if ( offset == kTableAllocFailed ) { return kTableAllocFailed; } - worker->next = offset; - worker->end = offset + kTableSlabBytes; - if ( worker->next == 0 ) { worker->next = kTableAlign; } /* offset 0 is null: the arena's head stays reserved */ + uint32_t offset=table_arena_grab_slab(worker->arena); + if(offset==kTableAllocFailed) { return kTableAllocFailed; } + worker->next=offset; worker->end=offset+kTableSlabBytes; + if(worker->next==0) { worker->next=kTableAlign; } + if((uint64_t)worker->next+(uint64_t)bytes>worker->end) { return table_arena_grab_span(worker->arena,bytes); } } - at = worker->next; - worker->next += bytes; - return at; + at=worker->next; worker->next+=(uint32_t)bytes; return at; } +static SCHEMA_UNUSED uint32_t table_worker_bump( TableWorker * worker, uint32_t bytes ) +{ return table_worker_alloc(worker,bytes); } /* ---- the resolution context: which encoding a walk is reading ---- @@ -729,6 +976,469 @@ typedef struct TableSink #endif /* SCHEMA_GRAPHDEMO_TABLE_ARENA */ +#ifndef SCHEMA_TABLE_BLOB_RUNTIME +#define SCHEMA_TABLE_BLOB_RUNTIME +typedef struct TableBlob { uint32_t length, zero; } TableBlob; +typedef struct TableBytesView { const uint8_t * data; int64_t length; } TableBytesView; +typedef struct TableStringView { const char * data; int64_t length; } TableStringView; +#define kTableBytesTypeId UINT64_C(0x2f2ec0474f1c4fe4) +#define kTableStringTypeId UINT64_C(0x704be0d8faaffc58) + +static SCHEMA_UNUSED int64_t table_blob_storage( int64_t length, int terminated ) +{ + if ( length < 0 || (uint64_t)length > UINT32_MAX ) { return -1; } + return table_align_up64(8+length+(terminated ? 1 : 0)); +} + +static SCHEMA_UNUSED const TableBlob * table_blob_at( const TableCtx * ctx, const TableRef * ref ) +{ + if ( ref->value == 0 ) { return NULL; } + return (const TableBlob *)(const void *)(ctx != NULL && ctx->arena != NULL + ? table_arena_at(ctx->arena,(uint32_t)ref->value) : (const uint8_t *)(const void *)ref+ref->value); +} +static SCHEMA_UNUSED TableBytesView table_bytes_at( const TableCtx * ctx, const TableRef * ref ) +{ + const TableBlob * blob=table_blob_at(ctx,ref); + TableBytesView view={NULL,0}; + if(blob!=NULL) { view.data=(const uint8_t *)(blob+1); view.length=blob->length; } return view; +} +static SCHEMA_UNUSED TableStringView table_string_at( const TableCtx * ctx, const TableRef * ref ) +{ + const TableBlob * blob=table_blob_at(ctx,ref); + TableStringView view={NULL,0}; + if(blob!=NULL) { view.data=(const char *)(blob+1); view.length=blob->length; } return view; +} +static SCHEMA_UNUSED TableBlob * table_blob_emplace( TableWorker * worker, TableRef * ref, int64_t length, int terminated ) +{ + int64_t bytes=table_blob_storage(length,terminated); + uint32_t at; + TableBlob * blob; + if(bytes<0) { return NULL; } + at=table_worker_alloc(worker,bytes); + if(at==kTableAllocFailed) { return NULL; } + blob=(TableBlob *)(void *)table_arena_at(worker->arena,at); + blob->length=(uint32_t)length; blob->zero=0; ref->value=at; return blob; +} +static SCHEMA_UNUSED uint8_t * table_bytes_emplace( TableWorker * worker, TableRef * ref, int64_t length ) +{ + TableBlob * blob=table_blob_emplace(worker,ref,length,0); + return blob!=NULL ? (uint8_t *)(blob+1) : NULL; +} +static SCHEMA_UNUSED char * table_string_emplace( TableWorker * worker, TableRef * ref, const char * text, int64_t length ) +{ + TableBlob * blob=table_blob_emplace(worker,ref,length,1); + char * data; + if(blob==NULL) { return NULL; } + data=(char *)(blob+1); if(text!=NULL && length>0) { memcpy(data,text,(size_t)length); } data[length]=0; return data; +} +#endif + +#ifndef SCHEMA_TABLE_GRAPH_RUNTIME +#define SCHEMA_TABLE_GRAPH_RUNTIME +typedef struct TableNumbering TableNumbering; +typedef struct TableNodeType +{ + uint64_t id; + int64_t size; + int (*save)( TableWriter * w, const void * value ); + int (*edges)( TableNumbering * numbering, const void * value ); + int blob; /* 0 table, 1 bytes, 2 terminated UTF-8 */ + int (*extent)(TableNumbering *,const void *,uint8_t *,int64_t *); + int64_t (*wire_storage)(const TableReader *); + int64_t alignment; + int has_extent; + int (*cook_body)(struct TableNumbering *,uint8_t *,const void *,int); + int (*cook_extent)(TableNumbering *,const void *,uint8_t *,uint8_t *,int64_t *,int); +} TableNodeType; + +typedef struct TableNodeEntry +{ + const void * value; + const TableNodeType * type; + uint64_t packed_offset; + int open; +} TableNodeEntry; + +typedef struct TableNodeWork +{ + const void * value; + const TableNodeType * type; + uint64_t index; + const TableRef * slot; + int close; +} TableNodeWork; + +struct TableNumbering +{ + const TableCtx * ctx; + uint8_t * pack_base; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + TableSequenceOrder * orders; +#endif + TableAllocator allocator; + TableNodeEntry * entries; + uint64_t count, capacity; + uint64_t * slots; + uint64_t slot_capacity; + TableNodeWork * work; + uint64_t work_count, work_capacity; +}; + +static SCHEMA_UNUSED int64_t table_node_storage( TableNumbering * n, const TableNodeType * type, const void * value ) +{ + int64_t at=type->size; + if(type->blob) { return table_blob_storage(((const TableBlob *)value)->length,type->blob==2); } + if(type->has_extent) { + int64_t extent=0; + if(!type->cook_extent(n,value,NULL,NULL,&extent,1) || at>INT64_MAX-kTableAlign) { return -1; } + at=table_align_up64(at); if(extent>INT64_MAX-at) { return -1; } at+=extent; + } + if(at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int64_t table_node_wire_storage( const TableNodeType * type, const TableReader * body ) +{ + if(type->blob && body->size>UINT32_MAX) { + body->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return -1; + } + return type->blob ? table_blob_storage(body->size,type->blob==2) : type->wire_storage(body); +} + +static SCHEMA_UNUSED int table_blob_save( TableWriter * w, const void * value ) +{ + const TableBlob * blob=(const TableBlob *)value; + table_writer_raw(w,blob+1,blob->length); return !w->overflow; +} +static SCHEMA_UNUSED int table_blob_edges( TableNumbering * n, const void * value ) +{ (void)n; (void)value; return 1; } +static const TableNodeType table_bytes_node_type = { kTableBytesTypeId, 0, table_blob_save, table_blob_edges, 1, NULL, NULL, 8, 0, NULL, NULL }; +static const TableNodeType table_string_node_type = { kTableStringTypeId, 0, table_blob_save, table_blob_edges, 2, NULL, NULL, 8, 0, NULL, NULL }; + + +static SCHEMA_UNUSED uint64_t table_number_hash( const void * value ) +{ + uint64_t x = (uint64_t)(uintptr_t)value; + x ^= x >> 33; x *= UINT64_C(0xff51afd7ed558ccd); + x ^= x >> 33; x *= UINT64_C(0xc4ceb9fe1a85ec53); + return x ^ (x >> 33); +} + +static SCHEMA_UNUSED void * table_number_grow( TableAllocator allocator, void * memory, uint64_t * capacity, size_t width ) +{ + uint64_t next = *capacity ? *capacity * 2 : 64; + void * grown; + if ( next < *capacity || next > INT64_MAX / width ) { return 0; } + grown = table_allocate(allocator,(int64_t)next*(int64_t)width); + if ( grown == NULL ) { return 0; } + if(memory!=NULL) { memcpy(grown,memory,(size_t)*capacity*width); } + table_release(allocator,memory); *capacity = next; return grown; +} + +static SCHEMA_UNUSED int table_number_rehash( TableNumbering * n ) +{ + uint64_t capacity = n->slot_capacity ? n->slot_capacity * 2 : 128; + uint64_t * slots; + uint64_t i; + if ( capacity < n->slot_capacity || capacity > INT64_MAX / sizeof(uint64_t) ) { return 0; } + slots = (uint64_t *)table_allocate(n->allocator,(int64_t)capacity*sizeof(uint64_t)); + if ( slots == NULL ) { return 0; } + for ( i = 0; i < n->count; i++ ) + { + uint64_t at = table_number_hash(n->entries[i].value) & (capacity-1); + while ( slots[at] ) { at = (at+1) & (capacity-1); } + slots[at] = i+1; + } + table_release(n->allocator,n->slots); n->slots = slots; n->slot_capacity = capacity; return 1; +} + +static SCHEMA_UNUSED uint64_t table_number_find( const TableNumbering * n, const void * value ) +{ + uint64_t at; + if ( value == NULL || n == NULL || n->slot_capacity == 0 ) { return 0; } + at = table_number_hash(value) & (n->slot_capacity-1); + while ( n->slots[at] ) + { + uint64_t index = n->slots[at]; + if ( n->entries[index-1].value == value ) { return index; } + at = (at+1) & (n->slot_capacity-1); + } + return 0; +} + +static SCHEMA_UNUSED int table_number_edge( TableNumbering * n, const void * value, const TableNodeType * type ) +{ + TableNodeWork entry; + if ( value == NULL ) { return 1; } + if ( n->work_count == n->work_capacity ) + { + void * grown = table_number_grow(n->allocator,n->work,&n->work_capacity,sizeof(TableNodeWork)); + if ( grown == NULL ) { return 0; } + n->work = (TableNodeWork *)grown; + } + entry.value = value; entry.type = type; entry.index = 0; entry.close = 0; entry.slot = NULL; + n->work[n->work_count++] = entry; return 1; +} + +static SCHEMA_UNUSED void table_number_dispose( TableNumbering * n ) +{ +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + table_sequence_orders_release(n->allocator,n->orders); +#endif + table_release(n->allocator,n->entries); table_release(n->allocator,n->slots); table_release(n->allocator,n->work); + memset(n,0,sizeof(*n)); +} + +static SCHEMA_UNUSED int table_number_build( TableNumbering * n, const TableCtx * ctx, + const void * root, const TableNodeType * type, TableAllocator allocator ) +{ + memset(n,0,sizeof(*n)); n->ctx = ctx; n->allocator=allocator; + if ( root == NULL || !table_number_edge(n,root,type) ) { return 0; } + while ( n->work_count ) + { + TableNodeWork visit = n->work[--n->work_count]; + uint64_t index, at, first, last; + TableNodeEntry entry; + if ( visit.close ) { n->entries[visit.index].open = 0; continue; } + index = table_number_find(n,visit.value); + if ( index ) + { + if ( n->entries[index-1].open || n->entries[index-1].type->id != visit.type->id ) { return 0; } + continue; + } + if ( n->slot_capacity == 0 || n->count >= n->slot_capacity/2 ) + { if ( !table_number_rehash(n) ) { return 0; } } + if ( n->count == n->capacity ) + { + void * grown = table_number_grow(n->allocator,n->entries,&n->capacity,sizeof(TableNodeEntry)); + if ( grown == NULL ) { return 0; } + n->entries = (TableNodeEntry *)grown; + } + entry.value = visit.value; entry.type = visit.type; entry.packed_offset = 0; entry.open = 1; + visit.index = n->count; visit.close = 1; + n->entries[n->count++] = entry; + at = table_number_hash(visit.value) & (n->slot_capacity-1); + while ( n->slots[at] ) { at = (at+1) & (n->slot_capacity-1); } + n->slots[at] = n->count; + if ( n->work_count == n->work_capacity ) + { + void * grown = table_number_grow(n->allocator,n->work,&n->work_capacity,sizeof(TableNodeWork)); + if ( grown == NULL ) { return 0; } + n->work = (TableNodeWork *)grown; + } + n->work[n->work_count++] = visit; + first = n->work_count; + if ( !visit.type->edges(n,visit.value) ) { return 0; } + last = n->work_count; + // Callbacks emit edges in declaration order. Reverse only this set + // so the LIFO work stack visits its first edge first, before siblings. + while ( first < last && first < --last ) + { + TableNodeWork swap = n->work[first]; + n->work[first++] = n->work[last]; n->work[last] = swap; + } + } + table_release(n->allocator,n->work); n->work = NULL; n->work_capacity = 0; + return 1; +} + + +static SCHEMA_UNUSED const void * table_ref_at( const TableCtx * ctx, const TableRef * slot ) +{ + if ( slot->value == 0 ) { return NULL; } + if ( ctx != NULL && ctx->arena != NULL ) { return table_arena_at(ctx->arena,(uint32_t)slot->value); } + return (const uint8_t *)(const void *)slot + slot->value; +} + +static SCHEMA_UNUSED int table_number_slot( TableNumbering * n, const TableRef * slot, const TableNodeType * type ) +{ + const void * value = table_ref_at(n->ctx,slot); + if ( value == NULL ) { return 1; } + if ( !table_number_edge(n,value,type) ) { return 0; } + n->work[n->work_count-1].slot = slot; + return 1; +} + +static SCHEMA_UNUSED int table_graph_records( TableWriter * w ) +{ + uint64_t i; + const TableNumbering * n = w->nodes; + table_writer_leb(w,n->count-1); + for ( i=1; icount; i++ ) + { + const TableNodeEntry * entry=&n->entries[i]; + table_writer_id(w,entry->type->id); + if ( w->buffer == NULL ) + { + int64_t begin=w->offset; + if ( !entry->type->save(w,entry->value) ) { return 0; } + table_writer_leb(w,(uint64_t)(w->offset-begin)); + } + else + { + TableWriter probe=table_writer_probe(w); + if ( !entry->type->save(&probe,entry->value) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb(w,(uint64_t)probe.offset); + if ( !entry->type->save(w,entry->value) ) { return 0; } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int64_t table_graph_save( const TableCtx * ctx, const void * root, + const TableNodeType * type, uint8_t * buffer, int64_t capacity, TableAllocator allocator ) +{ + TableNumbering numbering; + TableWriteIds vocabulary; + TableWriter w=table_writer_make(buffer,capacity,&vocabulary); + int64_t result=-1; + if ( !table_number_build(&numbering,ctx,root,type,allocator) ) { table_number_dispose(&numbering); return -1; } + w.nodes=&numbering; + table_writer_put8(&w,1); + if ( !type->save(&w,root) ) { goto done; } + w.offset--; // the root terminator follows the numbering's final field + if ( numbering.count>1 ) + { + table_writer_id(&w,UINT64_MAX); table_writer_put8(&w,12); + if ( buffer == NULL ) + { + int64_t begin=w.offset; + if ( !table_graph_records(&w) ) { goto done; } + table_writer_leb(&w,(uint64_t)(w.offset-begin)); + } + else + { + TableWriter probe=table_writer_probe(&w); + if ( !table_graph_records(&probe) ) { goto done; } + table_writer_rewind(&probe); table_writer_leb(&w,(uint64_t)probe.offset); + if ( !table_graph_records(&w) ) { goto done; } + } + } + table_writer_put8(&w,0); table_writer_finish(&w); + if ( !w.overflow ) { result=w.offset; } +done: + table_number_dispose(&numbering); return result; +} + +static SCHEMA_UNUSED uint8_t * table_graph_pack( const TableCtx * ctx, const void * root, + const TableNodeType * type, int64_t * bytes ) +{ + TableNumbering n; + uint64_t i; + int64_t total=0; + uint8_t * packed=NULL; + *bytes=0; + if ( !table_number_build(&n,ctx,root,type,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()) ) { table_number_dispose(&n); return NULL; } + for ( i=0; iINT64_MAX-total ) { goto done; } + n.entries[i].packed_offset=(uint64_t)total; total+=size; + } + if ( (uint64_t)total>SIZE_MAX ) { goto done; } + packed=(uint8_t *)table_allocate(n.allocator,total); + if ( packed==NULL ) { goto done; } + memset(packed,0,(size_t)total); + for ( i=0; ipacked_offset; + n.pack_base=packed; + if(entry->type->blob) { + memcpy(target,entry->value,(size_t)(8+(int64_t)((const TableBlob *)entry->value)->length)); + } else { + const uint16_t one=1; + int order=*(const uint8_t *)(const void *)&one ? 1 : 2; + int64_t at=0; + if(!entry->type->cook_body(&n,target,entry->value,order) || + (entry->type->has_extent && !entry->type->cook_extent(&n,entry->value,target,target+table_align_up64(entry->type->size),&at,order))) { + table_release(n.allocator,packed); packed=NULL; goto done; + } + } + } + *bytes=total; +done: + table_number_dispose(&n); return packed; +} +typedef struct TableNodeDirEntry { uint64_t offset, type_id; } TableNodeDirEntry; +typedef struct TableNodeMap +{ + uint8_t * base; + const TableNodeDirEntry * entries; + uint64_t count; + int good, arena, refused; + TableSink * sink; +} TableNodeMap; + +static SCHEMA_UNUSED void table_node_resolve( const TableNodeMap * nodes, TableRef * slot, + uint64_t index, uint64_t target, TableReport * report ) +{ + const TableNodeDirEntry * entry; + slot->value = 0; + if ( index == 0 || nodes == NULL || !nodes->good ) { return; } + if ( index > nodes->count ) { report->malformed = 1; return; } + entry = &nodes->entries[index-1]; + if ( entry->offset == UINT64_MAX ) { return; } + if ( entry->type_id != target ) { report->kind_mismatch++; return; } + slot->value = nodes->arena ? (int64_t)entry->offset + : (int64_t)((nodes->base + entry->offset) - (uint8_t *)(void *)slot); +} + +typedef struct TableNodeScan +{ + TableReader fields, payload; + uint64_t declared, records; + int opened, present, malformed; +} TableNodeScan; + +static SCHEMA_UNUSED TableNodeScan table_node_scan_begin( const TableReader * root ) +{ + TableNodeScan scan; + memset(&scan,0,sizeof(scan)); scan.fields = *root; scan.fields.offset = 0; + return scan; +} + +static SCHEMA_UNUSED int table_node_scan_open( TableNodeScan * scan ) +{ + TableReader * fields = &scan->fields; + if ( scan->opened ) { return 0; } + scan->opened = 1; + for ( ;; ) + { + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb(fields,&ref) || ref == 0 || ref > fields->id_count ) { break; } + id = table_reader_id_at(fields,ref); + if ( !table_reader_has(fields,1) ) { break; } + kind = table_reader_get8(fields); + if ( id == UINT64_MAX ) + { + scan->present = 1; + if ( kind != 12 || !table_reader_span(fields,&scan->payload) ) + { scan->malformed = 1; return 0; } + } + else if ( !table_reader_skip(fields,kind) ) { break; } + } + if ( !scan->present ) { return 0; } + if ( !table_reader_leb(&scan->payload,&scan->declared) ) { scan->malformed = 1; return 0; } + return 1; +} + +static SCHEMA_UNUSED int table_node_scan_next( TableNodeScan * scan, uint64_t * type_id, TableReader * body ) +{ + uint64_t ref; + if ( !scan->opened && !table_node_scan_open(scan) ) { return 0; } + if ( scan->malformed || scan->payload.offset >= scan->payload.size ) { return 0; } + if ( !table_reader_leb(&scan->payload,&ref) || ref == 0 || ref > scan->payload.id_count || + !table_reader_span(&scan->payload,body) ) { scan->malformed = 1; return 0; } + *type_id = table_reader_id_at(&scan->payload,ref); scan->records++; + return 1; +} + +static SCHEMA_UNUSED int table_node_scan_whole( const TableNodeScan * scan ) +{ + return !scan->malformed && (!scan->present || scan->declared == scan->records); +} +#endif + #ifndef SCHEMA_GRAPHDEMO_BUILD_VERSION #define SCHEMA_GRAPHDEMO_BUILD_VERSION @@ -840,25 +1550,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -868,60 +1578,1392 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_GRAPHDEMO_TABLE_COOK */ -/* table Meta — TABLE-wire storage: relocatable, bounded. C has no member - initializers, so the declared defaults live in meta_reset and nowhere - else — one definition of what a default is (docs/SPEC-TABLES.md) */ -typedef struct Meta { - int32_t build; - char tag[8 + 1]; /* string(8): N + 1 for the terminator the wire does not carry */ - int32_t tag_length; -} Meta; - -/* table Settings — TABLE-wire storage: relocatable, bounded. C has no member - initializers, so the declared defaults live in settings_reset and nowhere - else — one definition of what a default is (docs/SPEC-TABLES.md) */ -typedef struct Settings { - int32_t quality; - char label[16 + 1]; /* string(16): N + 1 for the terminator the wire does not carry */ - int32_t label_length; -} Settings; +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) +{ + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} -/* table ListNode — TABLE-wire storage: relocatable, bounded. C has no member - initializers, so the declared defaults live in list_node_reset and nowhere +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused;TableNodeMap * nodes; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + +#ifndef SCHEMA_TABLE_COOK_GRAPH_RUNTIME +#define SCHEMA_TABLE_COOK_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_cook_ref(TableNumbering * n,uint8_t * at,const TableRef * slot,int order) +{ + const void * pointee=table_ref_at(n->ctx,slot); + uint64_t index=table_number_find(n,pointee); + if(pointee!=NULL && index==0) { return 0; } + table_cook_put(at,index ? n->entries[index-1].packed_offset-(uint64_t)(at-n->pack_base) : 0,8,order); + return 1; +} +static SCHEMA_UNUSED int64_t table_cook_layout(TableNumbering * n,int64_t * alignment,int64_t * data_bytes) +{ + uint64_t i; + int64_t total=0, align=8; + for(i=0;icount;i++) { + TableNodeEntry * entry=&n->entries[i]; const TableNodeType * type=entry->type; + int64_t size=type->size, node_align=type->alignment; + if(type->blob) { size=8+(int64_t)((const TableBlob *)entry->value)->length+(type->blob==2); } + else if(type->has_extent) { + int64_t extent=0; + if(!type->cook_extent(n,entry->value,NULL,NULL,&extent,TableByteOrder_Little) || size>INT64_MAX-7) { return -1; } + size=(size+7)&~INT64_C(7); if(extent>INT64_MAX-size) { return -1; } size+=extent; + } + if(node_align<1 || total>INT64_MAX-(node_align-1)) { return -1; } + total=(total+node_align-1)&~(node_align-1); + entry->packed_offset=(uint64_t)total; + if(size<0 || size>INT64_MAX-total) { return -1; } total+=size; + if(node_align>align) { align=node_align; } + } + if(total>INT64_MAX-(align-1)) { return -1; } total=(total+align-1)&~(align-1); + *alignment=align; *data_bytes=total; + if(total>INT64_MAX-64 || n->count>(uint64_t)(INT64_MAX-64-total)/16) { return -1; } + return 64+total+(int64_t)n->count*16; +} +static SCHEMA_UNUSED int64_t table_graph_cook(const TableCtx * ctx,const void * root,const TableNodeType * type, + void * output,uint64_t capacity,int order,TableAllocator allocator,uint64_t version,int measure) +{ + TableNumbering n; int64_t result=-1,align=8,bytes=0,need; uint64_t i; uint8_t * raw=(uint8_t *)output; + if(!table_number_build(&n,ctx,root,type,allocator)) { table_number_dispose(&n); return -1; } + need=table_cook_layout(&n,&align,&bytes); if(need<0) { goto done; } + if(measure) { result=need; goto done; } + if(raw==NULL || (uint64_t)need>capacity || (uint64_t)need>SIZE_MAX) { goto done; } + memset(raw,0,(size_t)need); n.pack_base=raw+64; + for(i=0;ipacked_offset; + if(entry->type->blob) { + const TableBlob * blob=(const TableBlob *)entry->value; + table_cook_put(at,blob->length,4,order); table_cook_bytes(at+8,blob+1,blob->length,blob->length); + } else { + int64_t extent=0; + if(!entry->type->cook_body(&n,at,entry->value,order)) { goto done; } + if(entry->type->has_extent) { + int64_t expected=0,record=(entry->type->size+7)&~INT64_C(7); + if(!entry->type->cook_extent(&n,entry->value,NULL,NULL,&expected,order) || + !entry->type->cook_extent(&n,entry->value,at,at+record,&extent,order) || extent!=expected) { goto done; } + } + } + table_cook_put(n.pack_base+bytes+i*16,entry->packed_offset,8,order); + table_cook_put(n.pack_base+bytes+i*16+8,entry->type->id,8,order); + } + /* A failed body must never leave a recognizable cook header. */ + table_cook_header(raw,version,bytes,(int64_t)n.count,align,order); result=need; +done: + table_number_dispose(&n); return result; +} +#endif + +#ifndef SCHEMA_TABLE_RETAIN_RUNTIME +#define SCHEMA_TABLE_RETAIN_RUNTIME +#define SCHEMA_TABLE_RETAIN_DEPTH_MAX 1 +#define SCHEMA_TABLE_RETAIN_WALK_MAX 64 +#define SCHEMA_TABLE_RETAIN_HEADER 26 + +typedef struct TableRetainId { uint64_t id; int32_t slot; } TableRetainId; +typedef struct TableRetain { + uint8_t * bytes; int64_t capacity,used; + TableRetainId * ids; int32_t id_capacity,id_used,count; + const uint8_t * base; const TableNodeDirEntry * directory; int64_t directory_count; +} TableRetain; +typedef struct TableRetainStep {uint32_t ordinal,index;} TableRetainStep; +typedef struct TableRetainPath { + const void * at; uint32_t node; int32_t depth; + TableRetainStep steps[SCHEMA_TABLE_RETAIN_DEPTH_MAX]; +} TableRetainPath; +typedef struct TableRetainIds { + TableWriteIds known; + int32_t slots[sizeof(((TableWriteIds *)0)->ids)/sizeof(uint64_t)]; + TableRetain * retain; int32_t count; int lost; +} TableRetainIds; +typedef struct TableRetainWalk {TableRetain * retain;TableRetainIds * ids;TableRetainPath path;} TableRetainWalk; +static const uint64_t table_retain_known[]={UINT64_C(0x0a8f12cc5f9a0c03),UINT64_C(0x1e4984ef2e958a4c),UINT64_C(0x24b070ada2041cb0),UINT64_C(0x24f3a319b88552c1),UINT64_C(0x2f2ec0474f1c4fe4),UINT64_C(0x327fe6dc702553fd),UINT64_C(0x39f7fcec8fcb623d),UINT64_C(0x3bf8fbbad1587cdd),UINT64_C(0x4320e9a2e32eac38),UINT64_C(0x4339ee8ab21c8380),UINT64_C(0x4554e34a747022df),UINT64_C(0x4a9a31623ab5f213),UINT64_C(0x509220bb65a646b7),UINT64_C(0x56d7ab194448a4f3),UINT64_C(0x5b25b8ef511eb395),UINT64_C(0x60839e2395be697e),UINT64_C(0x69ff34904242a73d),UINT64_C(0x6dadeaaee49d6d18),UINT64_C(0x704be0d8faaffc58),UINT64_C(0x732dfbcc9b0cf0bb),UINT64_C(0x75d8e97600b296ea),UINT64_C(0x76aaaa535714d805),UINT64_C(0x77af761956600b54),UINT64_C(0x7a8060916400fe66),UINT64_C(0x7ce4fd9430e80cea),UINT64_C(0x7e9d0b96d39e517d),UINT64_C(0x802517e298c70b03),UINT64_C(0x823b8a195ce2133c),UINT64_C(0x9d167ef77aed79b6),UINT64_C(0x9d8b8aa2b404c2c8),UINT64_C(0x9deeefd89ca8a81d),UINT64_C(0xae3b9113b7db93a4),UINT64_C(0xaf63da4c8601e926),UINT64_C(0xaf63df4c8601f1a5),UINT64_C(0xaf63ef4c86020cd5),UINT64_C(0xb97e90a3784c431d),UINT64_C(0xbb62c62c9808ea37),UINT64_C(0xc4bcadba8e631b86),UINT64_C(0xd6458a3eef83d457),UINT64_C(0xd858c2cb7f1514cc),UINT64_C(0xdd9eb981e152e90c),UINT64_C(0xe5316cbaa025f028),UINT64_C(0xeddcb72b15486e77),UINT64_C(0xee5f6d7b48b44de8),UINT64_C(0xee7ba9ad45c64144),UINT64_C(0xf60ec899a5a69fa9),UINT64_C(0xffffffffffffffff),}; +static SCHEMA_UNUSED uint32_t table_retain_u32(const uint8_t * p) +{return (uint32_t)p[0]|(uint32_t)p[1]<<8|(uint32_t)p[2]<<16|(uint32_t)p[3]<<24; +} +static SCHEMA_UNUSED uint64_t table_retain_u64(const uint8_t * p) +{return table_retain_u32(p)|(uint64_t)table_retain_u32(p+4)<<32; +} +static SCHEMA_UNUSED void table_retain_put32(uint8_t * p,uint32_t v) +{int i; +for(i=0;i<4;i++)p[i]=(uint8_t)(v>>(8*i)); +} +static SCHEMA_UNUSED void table_retain_put64(uint8_t * p,uint64_t v) +{table_retain_put32(p,(uint32_t)v); +table_retain_put32(p+4,(uint32_t)(v>>32)); +} +static SCHEMA_UNUSED int64_t table_retain_leb_bytes(uint64_t v) +{int64_t n=1; +while(v>=128){v>>=7; +n++; +}return n; +} +static SCHEMA_UNUSED int table_retain_reserved(uint64_t id) +{return id>=UINT64_MAX-2; +} +static SCHEMA_UNUSED TableRetainPath table_retain_root(const void * at,uint32_t node) +{TableRetainPath p; +memset(&p,0,sizeof(p)); +p.at=at; +p.node=node; +return p; +} +static SCHEMA_UNUSED TableRetainWalk table_retain_step(TableRetainWalk keep,uint32_t ordinal,uint32_t index) +{if(keep.path.depth0&&keep.path.depth<=SCHEMA_TABLE_RETAIN_DEPTH_MAX)keep.path.steps[keep.path.depth-1].index=index; +return keep; +} +static SCHEMA_UNUSED uint8_t * table_retain_payload(uint8_t * record) +{return record+SCHEMA_TABLE_RETAIN_HEADER+(int64_t)table_retain_u32(record+8)*8; +} +static SCHEMA_UNUSED const void * table_retain_at(const TableRetain * retain,const uint8_t * record) +{uint32_t node=table_retain_u32(record+4); +if(!retain->directory||!node||node>retain->directory_count)return NULL; +return retain->base+retain->directory[node-1].offset; +} +static SCHEMA_UNUSED int table_retain_under(const TableRetain * retain,const uint8_t * record,const TableRetainPath * path,int field,uint32_t ordinal) +{ + uint32_t depth=table_retain_u32(record+8); + int32_t i; + const uint8_t * steps=record+SCHEMA_TABLE_RETAIN_HEADER; + if(path->depth<0||path->depth>SCHEMA_TABLE_RETAIN_DEPTH_MAX||table_retain_at(retain,record)!=path->at)return 0; + if(depth<(uint32_t)path->depth||(field&&depth==(uint32_t)path->depth))return 0; + for(i=0;idepth;i++)if(table_retain_u32(steps+8*i)!=path->steps[i].ordinal||table_retain_u32(steps+8*i+4)!=path->steps[i].index)return 0; + return !field||table_retain_u32(steps+8*path->depth)==ordinal; +} +static SCHEMA_UNUSED int table_retain_here(const TableRetain * retain,const uint8_t * record,const TableRetainPath * path) +{return table_retain_u32(record+8)==(uint32_t)path->depth&&table_retain_under(retain,record,path,0,0); +} +static SCHEMA_UNUSED void table_retain_discard(TableRetainWalk keep,int field,uint32_t ordinal) +{ + TableRetain * r=keep.retain; + int64_t read=0,write=0; + int32_t i,kept=0; + if(!r||!r->bytes)return; + for(i=0;icount;i++){uint32_t n=table_retain_u32(r->bytes+read); + if(!table_retain_under(r,r->bytes+read,&keep.path,field,ordinal)){if(read!=write)memmove(r->bytes+write,r->bytes+read,n); + write+=n; + kept++; + }read+=n; + }r->used=write; + r->count=kept; +} +static SCHEMA_UNUSED void table_retain_reset(TableRetain * retain,const TableNodeMap * nodes,const uint8_t * region) +{if(retain){retain->used=0; +retain->id_used=0; +retain->count=0; +retain->base=region; +retain->directory=nodes?nodes->entries:NULL; +retain->directory_count=nodes?(int64_t)nodes->count:0; +}} +static SCHEMA_UNUSED int table_retain_nameable(uint64_t id) +{int64_t lo=0,hi=(int64_t)(sizeof(table_retain_known)/sizeof(table_retain_known[0]))-1; +while(lo<=hi){int64_t mid=lo+(hi-lo)/2; +if(table_retain_known[mid]==id)return 1; +if(table_retain_known[mid]retain; + TableWriteIds * v=&ids->known; + if(record&&!table_retain_nameable(id)){ + if(!r){ids->lost=1; + return 0; + }for(i=0;iid_used;i++)if(r->ids[i].id==id)return (uint64_t)r->ids[i].slot; + if(!r->ids||r->id_used>=r->id_capacity||ids->count==INT32_MAX){ids->lost=1; + return 0; + } + r->ids[r->id_used].id=id; + r->ids[r->id_used++].slot=++ids->count; + return (uint64_t)ids->count; + } + for(i=0;icount;i++)if(v->ids[i]==id)return (uint64_t)ids->slots[i]; + if(v->count==(int)(sizeof(v->ids)/sizeof(v->ids[0]))||ids->count==INT32_MAX){*overflow=1; + return 0; + } + v->ids[v->count]=id; + ids->slots[v->count++]=++ids->count; + return (uint64_t)ids->count; +} +static SCHEMA_UNUSED void table_retain_truncate(TableRetainIds * ids,int32_t mark) +{while(ids->known.count&&ids->slots[ids->known.count-1]>mark)ids->known.count--; +if(ids->retain)while(ids->retain->id_used&&ids->retain->ids[ids->retain->id_used-1].slot>mark)ids->retain->id_used--; +ids->count=mark; +} +static SCHEMA_UNUSED void table_retain_writer_id(TableWriter * w,uint64_t id,TableRetainWalk keep) +{table_writer_leb(w,table_retain_ref(keep.ids,id,0,&w->overflow)); +} +static SCHEMA_UNUSED TableWriter table_retain_probe(const TableWriter * w,TableRetainWalk keep) +{TableWriter p=*w; +p.buffer=NULL; +p.capacity=INT64_MAX; +p.offset=0; +p.overflow=0; +p.id_checkpoint=keep.ids->count; +return p; +} +static SCHEMA_UNUSED void table_retain_rewind(const TableWriter * probe,TableRetainWalk keep) +{table_retain_truncate(keep.ids,probe->id_checkpoint); +} +static SCHEMA_UNUSED void table_retain_finish(TableWriter * w,TableRetainIds * ids) +{int32_t i=0,j=0; +TableRetain * r=ids->retain; +while(iknown.count||(r&&jid_used)){if(!r||j>=r->id_used||(iknown.count&&ids->slots[i]ids[j].slot))table_writer_put64(w,ids->known.ids[i++]); +else table_writer_put64(w,r->ids[j++].id); +}table_writer_put64(w,(uint64_t)ids->count); +} + +/* Capture writes only within the remaining record capacity. Failure leaves + used/count unchanged; a huge zero-bit message count reaches this bound + before it can command an unbounded transcode. */ +typedef struct TableRetainIn { + TableReader input;uint8_t * out;int64_t capacity,used;int failed; +} TableRetainIn; +static SCHEMA_UNUSED int table_retain_raw(TableRetainIn * s,const void * data,int64_t bytes) +{if(s->failed||bytes<0||s->used>s->capacity||bytes>s->capacity-s->used){s->failed=1; +return 0; +}if(bytes)memcpy(s->out+s->used,data,(size_t)bytes); +s->used+=bytes; +return 1; +} +static SCHEMA_UNUSED int table_retain_leb(TableRetainIn * s,uint64_t v) +{uint8_t b[10]; +int n=0; +do{b[n++]=(uint8_t)((v&127)|(v>=128?128:0)); +v>>=7; +}while(v); +return table_retain_raw(s,b,n); +} +static SCHEMA_UNUSED int table_retain_id(TableRetainIn * s,uint64_t id) +{uint8_t b[8]; +table_retain_put64(b,id); +return table_retain_raw(s,b,8); +} +static SCHEMA_UNUSED int table_retain_in_ref(TableRetainIn * s,int zero) +{uint64_t ref,id; +if(!table_reader_leb(&s->input,&ref))return 0; +if(!ref)return zero&&table_retain_id(s,0); +if(ref>s->input.id_count)return 0; +id=table_reader_id_at(&s->input,ref); +return id&&!table_retain_reserved(id)&&table_retain_id(s,id); +} +static SCHEMA_UNUSED int table_retain_in_payload(TableRetainIn *,uint8_t,int32_t); +static SCHEMA_UNUSED int table_retain_in_content(TableRetainIn *,uint8_t,int64_t,int32_t); +static SCHEMA_UNUSED int table_retain_in_frame(TableRetainIn * s,uint8_t kind,int64_t length,int32_t depth) +{uint8_t zero[8]={0}; +int64_t mark=s->used; +if(!table_retain_raw(s,zero,8)||!table_retain_in_content(s,kind,length,depth))return 0; +if(s->used-mark-8>UINT32_MAX)return 0; +table_retain_put32(s->out+mark,(uint32_t)(s->used-mark-8)); +return 1; +} +static SCHEMA_UNUSED int table_retain_in_content(TableRetainIn * s,uint8_t kind,int64_t length,int32_t depth) +{ + int64_t end; + TableReader * r=&s->input; + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX||!table_reader_has(r,length))return 0; + end=r->offset+length; + switch(kind){ + case 13:for(;;){uint64_t ref; + int64_t mark=r->offset; + uint8_t k; + if(!table_reader_leb(r,&ref))return 0; + if(!ref){if(!table_retain_id(s,0))return 0; + break; + }r->offset=mark; + if(!table_retain_in_ref(s,0)||r->offset>=end)return 0; + k=table_reader_get8(r); + if(!table_retain_raw(s,&k,1)||!table_retain_in_payload(s,k,depth)||r->offset>end)return 0; + }break; + case 14:case 16:{uint8_t elem; + uint64_t n,i; + if(r->offset>=end)return 0; + elem=table_reader_get8(r); + if(!table_retain_raw(s,&elem,1)||!table_reader_leb(r,&n)||!table_retain_leb(s,n))return 0; + for(i=0;i(uint64_t)(end-r->offset)||!table_retain_in_frame(s,elem,(int64_t)bytes,depth+1))return 0; + }else if(!table_retain_in_payload(s,elem,depth))return 0; + if(r->offset>end)return 0; + }break; + } + case 15:case 30:if(!table_retain_in_payload(s,kind,depth))return 0; + break; + case 17:return 0; + default:if(!table_retain_raw(s,r->buffer+r->offset,length))return 0; + r->offset+=length; + break; + }return r->offset==end; +} +static SCHEMA_UNUSED int table_retain_width(uint8_t kind) +{switch(kind){case 1:case 2:case 6:case 20:case 25:return 1; +case 3:case 7:case 21:case 26:return 2; +case 4:case 8:case 10:case 22:case 27:return 4; +case 5:case 9:case 11:case 23:case 28:return 8; +case 18:case 19:case 24:case 29:return 16; +default:return 0; +}} +static SCHEMA_UNUSED int table_retain_in_payload(TableRetainIn * s,uint8_t kind,int32_t depth) +{ + TableReader * r=&s->input; + uint64_t length; + int width=table_retain_width(kind); + if(width){if(!table_reader_has(r,width)||!table_retain_raw(s,r->buffer+r->offset,width))return 0; + r->offset+=width; + return 1; + } + switch(kind){ + case 12:case 31:case 32:case 33:if(!table_reader_leb(r,&length)||!table_reader_room(r,length)||!table_retain_leb(s,length)||!table_retain_raw(s,r->buffer+r->offset,(int64_t)length))return 0; + r->offset+=(int64_t)length; + return 1; + case 13:case 14:case 16:if(!table_reader_leb(r,&length)||!table_reader_room(r,length))return 0; + return table_retain_in_frame(s,kind,(int64_t)length,depth+1); + case 15:{uint64_t arm; + uint8_t k; + int64_t mark=r->offset; + if(!table_reader_leb(r,&arm))return 0; + r->offset=mark; + if(!table_retain_in_ref(s,1))return 0; + if(!arm)return 1; + if(!table_reader_has(r,1))return 0; + k=table_reader_get8(r); + if(!table_retain_raw(s,&k,1)||!table_reader_leb(r,&length)||!table_reader_room(r,length))return 0; + return table_retain_in_frame(s,k,(int64_t)length,depth+1); + } + case 30:return table_retain_in_ref(s,1); + default:return 0; + } +} +static SCHEMA_UNUSED uint8_t * table_retain_begin(TableRetainWalk keep,uint64_t id,uint8_t kind,TableRetainIn * s) +{ + TableRetain * r=keep.retain; + int64_t header=SCHEMA_TABLE_RETAIN_HEADER+8*(int64_t)keep.path.depth; + uint8_t * p; + int32_t i; + if(table_retain_reserved(id)||!r||!r->bytes||keep.path.depth<0||keep.path.depth>SCHEMA_TABLE_RETAIN_DEPTH_MAX||r->used<0||r->used>r->capacity||header>r->capacity-r->used||r->count==INT32_MAX)return NULL; + p=r->bytes+r->used; + table_retain_put32(p+4,keep.path.node); + table_retain_put32(p+8,(uint32_t)keep.path.depth); + table_retain_put64(p+16,id); + p[24]=kind; + p[25]=0; + for(i=0;iout=p+header; + s->capacity=r->capacity-r->used-header; + if(s->capacity>UINT32_MAX-header)s->capacity=UINT32_MAX-header; + return p; +} +static SCHEMA_UNUSED void table_retain_commit(TableRetainWalk keep,uint8_t * record,const TableRetainIn * s,TableReport * report) +{int64_t bytes=SCHEMA_TABLE_RETAIN_HEADER+8*(int64_t)keep.path.depth+s->used; +table_retain_put32(record,(uint32_t)bytes); +table_retain_put32(record+12,(uint32_t)s->used); +keep.retain->used+=bytes; +keep.retain->count++; +report->retained++; +} +static SCHEMA_UNUSED int table_retain_capture(TableReader * r,uint64_t id,uint8_t kind,TableRetainWalk keep) +{ + TableRetainIn s; + TableReader source=*r; + uint8_t * record; + if(!table_reader_skip(r,kind))return 0; + if(!keep.retain)return 1; + record=table_retain_begin(keep,id,kind,&s); + if(!record){r->report->retain_lost++; + return 1; + }source.size=r->offset; + s.input=source; + if(!table_retain_in_payload(&s,kind,0)||s.input.offset!=source.size){r->report->retain_lost++; + return 1; + }table_retain_commit(keep,record,&s,r->report); + return 1; +} + + +typedef struct TableRetainOut { + uint8_t * input;int64_t size,at,bytes;TableRetainIds * ids;TableWriter * writer;int overflow; +} TableRetainOut; +static SCHEMA_UNUSED int table_retain_out_raw(TableRetainOut * s,const void * data,int64_t bytes) +{if(bytes<0||bytes>INT64_MAX-s->bytes)return 0; +if(s->writer)table_writer_raw(s->writer,data,bytes); +s->bytes+=bytes; +return !s->writer||!s->writer->overflow; +} +static SCHEMA_UNUSED int table_retain_out_leb(TableRetainOut * s,uint64_t v) +{if(s->writer)table_writer_leb(s->writer,v); +s->bytes+=table_retain_leb_bytes(v); +return !s->writer||!s->writer->overflow; +} +static SCHEMA_UNUSED int table_retain_out_leb_read(TableRetainOut * s,uint64_t * v) +{TableReader r=table_reader_make(s->input,s->size,NULL); +r.offset=s->at; +if(!table_reader_leb(&r,v))return 0; +s->at=r.offset; +return 1; +} +static SCHEMA_UNUSED int table_retain_out_ref(TableRetainOut * s) +{uint64_t id,ref; +if(s->size-s->at<8)return 0; +id=table_retain_u64(s->input+s->at); +s->at+=8; +if(!id)return table_retain_out_leb(s,0); +ref=table_retain_ref(s->ids,id,1,&s->overflow); +return !s->ids->lost&&!s->overflow&&table_retain_out_leb(s,ref); +} +static SCHEMA_UNUSED int table_retain_out_payload(TableRetainOut *,uint8_t,int32_t); +static SCHEMA_UNUSED int table_retain_out_content(TableRetainOut *,uint8_t,int64_t,int32_t); +static SCHEMA_UNUSED int table_retain_out_frame(TableRetainOut * s,uint8_t kind,int32_t depth) +{ + uint8_t * slot; + int64_t resolved,wire,begin; + if(s->size-s->at<8)return 0; + slot=s->input+s->at; + resolved=table_retain_u32(slot); + s->at+=8; + if(!s->writer){begin=s->bytes; + if(!table_retain_out_content(s,kind,resolved,depth))return 0; + wire=s->bytes-begin; + if(wire>UINT32_MAX)return 0; + table_retain_put32(slot+4,(uint32_t)wire); + s->bytes+=table_retain_leb_bytes((uint64_t)wire); + return 1; + } + wire=table_retain_u32(slot+4); + if(!table_retain_out_leb(s,(uint64_t)wire))return 0; + begin=s->bytes; + return table_retain_out_content(s,kind,resolved,depth)&&s->bytes-begin==wire; +} +static SCHEMA_UNUSED int table_retain_out_content(TableRetainOut * s,uint8_t kind,int64_t length,int32_t depth) +{ + int64_t end; + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX||length<0||length>s->size-s->at)return 0; + end=s->at+length; + switch(kind){ + case 13:for(;;){uint64_t id; + uint8_t k; + if(end-s->at<8)return 0; + id=table_retain_u64(s->input+s->at); + if(!table_retain_out_ref(s))return 0; + if(!id)break; + if(s->at>=end)return 0; + k=s->input[s->at++]; + if(!table_retain_out_raw(s,&k,1)||!table_retain_out_payload(s,k,depth))return 0; + }break; + case 14:case 16:{uint64_t n,i; + uint8_t elem; + if(s->at>=end)return 0; + elem=s->input[s->at++]; + if(!table_retain_out_raw(s,&elem,1)||!table_retain_out_leb_read(s,&n)||!table_retain_out_leb(s,n))return 0; + for(i=0;iinput+s->at,length))return 0; + s->at+=length; + break; + }return s->at==end; +} +static SCHEMA_UNUSED int table_retain_out_payload(TableRetainOut * s,uint8_t kind,int32_t depth) +{ + int width=table_retain_width(kind); + uint64_t length; + if(width){if(width>s->size-s->at||!table_retain_out_raw(s,s->input+s->at,width))return 0; + s->at+=width; + return 1; + } + switch(kind){ + case 12:case 31:case 32:case 33:if(!table_retain_out_leb_read(s,&length)||length>(uint64_t)(s->size-s->at)||!table_retain_out_leb(s,length)||!table_retain_out_raw(s,s->input+s->at,(int64_t)length))return 0; + s->at+=(int64_t)length; + return 1; + case 13:case 14:case 16:return table_retain_out_frame(s,kind,depth+1); + case 15:{uint64_t arm; + uint8_t k; + if(s->size-s->at<8)return 0; + arm=table_retain_u64(s->input+s->at); + if(!table_retain_out_ref(s))return 0; + if(!arm)return 1; + if(s->at>=s->size)return 0; + k=s->input[s->at++]; + return table_retain_out_raw(s,&k,1)&&table_retain_out_frame(s,k,depth+1); + } + case 30:return table_retain_out_ref(s); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t table_retain_record_measure(uint8_t * record,TableRetainIds * ids,uint64_t * ref) +{ + int32_t mark=ids->count; + TableRetainOut s; + int overflow=0; + ids->lost=0; + *ref=table_retain_ref(ids,table_retain_u64(record+16),1,&overflow); + if(!ids->lost&&!overflow){memset(&s,0,sizeof(s)); + s.input=table_retain_payload(record); + s.size=table_retain_u32(record+12); + s.ids=ids; + if(table_retain_out_payload(&s,record[24],0)&&s.at==s.size)return table_retain_leb_bytes(*ref)+1+s.bytes; + } + table_retain_truncate(ids,mark); + ids->lost=0; + return -1; +} +static SCHEMA_UNUSED int table_retain_tail(TableWriter * w,TableRetainWalk keep) +{ + TableRetain * r=keep.retain; + int32_t i; + int64_t at=0; + if(!r||!r->bytes)return 1; + for(i=0;icount;i++){uint8_t * record=r->bytes+at; + uint64_t ref; + int64_t bytes; + TableRetainOut s; + at+=table_retain_u32(record); + if(!table_retain_here(r,record,&keep.path))continue; + if(w->check_default){w->offset=2; + return 1; + } + bytes=table_retain_record_measure(record,keep.ids,&ref); + if(bytes<0)continue; + if(!w->buffer){if(bytes>INT64_MAX-w->offset){w->overflow=1; + return 0; + }w->offset+=bytes; + continue; + } + table_writer_leb(w,ref); + table_writer_put8(w,record[24]); + memset(&s,0,sizeof(s)); + s.input=table_retain_payload(record); + s.size=table_retain_u32(record+12); + s.ids=keep.ids; + s.writer=w; + if(!table_retain_out_payload(&s,record[24],0)||s.at!=s.size)return 0; + record[25]=1; + }return !w->overflow; +} +static SCHEMA_UNUSED void table_retain_clear_placed(TableRetain * r) +{int32_t i; +int64_t at=0; +if(!r||!r->bytes)return; +for(i=0;icount;i++){r->bytes[at+25]=0; +at+=table_retain_u32(r->bytes+at); +}} +static SCHEMA_UNUSED void table_retain_count_lost(TableRetain * r,TableReport * report) +{int32_t i; +int64_t at=0; +if(!r||!r->bytes)return; +for(i=0;icount;i++){if(!r->bytes[at+25])report->retain_lost++; +at+=table_retain_u32(r->bytes+at); +}} + + +typedef struct TableRetainMessage {TableRetainIn output;TableMessageReader input;} TableRetainMessage; +static SCHEMA_UNUSED int table_retain_message_payload(TableRetainMessage *,const TableMessageEntry *,int32_t); +static SCHEMA_UNUSED int table_retain_message_content(TableRetainMessage *,const TableMessageEntry *,int32_t); +static SCHEMA_UNUSED int table_retain_message_frame(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{uint8_t zero[8]={0}; +int64_t mark=s->output.used; +if(!table_retain_raw(&s->output,zero,8)||!table_retain_message_content(s,e,depth))return 0; +if(s->output.used-mark-8>UINT32_MAX)return 0; +table_retain_put32(s->output.out+mark,(uint32_t)(s->output.used-mark-8)); +return 1; +} +static SCHEMA_UNUSED int table_retain_message_scalar(TableRetainMessage * s,const TableMessageEntry * e) +{ + uint64_t lo=0,hi=0; + uint8_t bytes[16]; + int width=table_retain_width(e->kind); + if(!width)return 0; + if(e->kind==10&&e->packing==2){float value; + if(!table_message_float(&s->input,e,&value))return 0; + lo=table_float_to_bits(value); + } + else if(!table_message_number(&s->input,e,&lo,&hi))return 0; + table_retain_put64(bytes,lo); + table_retain_put64(bytes+8,hi); + return table_retain_raw(&s->output,bytes,width); +} +static SCHEMA_UNUSED int table_retain_message_opaque(TableRetainMessage * s,const TableMessageEntry * e,int framed) +{ + uint64_t n,i; + TableMessageReader * r=&s->input; + if(e->kind==12||e->kind==33){if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==33)n*=2; + else if(!table_bit_align_read(&r->bits))return 0; + } + else if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + if(n>INT64_MAX/8||!table_bit_has(&r->bits,(int64_t)n*8))return 0; + if(framed&&!table_retain_leb(&s->output,n))return 0; + if(n>(uint64_t)(s->output.capacity-s->output.used))return 0; + for(i=0;ibits,&v,8))return 0; + byte=(uint8_t)v; + if(!table_retain_raw(&s->output,&byte,1))return 0; + }return 1; +} +static SCHEMA_UNUSED int table_retain_message_content(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{ + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX)return 0; + switch(e->kind){ + case 13:for(;;){const TableMessageEntry * f; + if(!table_message_ref(&s->input,&f,0))return 0; + if(!f)return table_retain_id(&s->output,0); + if(!f->id||!table_retain_id(&s->output,f->id)||!table_retain_raw(&s->output,&f->kind,1)||!table_retain_message_payload(s,f,depth))return 0; + } + case 14:case 16:{uint64_t n,i; + TableMessageEntry elem=table_message_element(e); + if(elem.kind==17||!table_message_count(&s->input,e,&n)||!table_retain_raw(&s->output,&elem.kind,1)||!table_retain_leb(&s->output,n))return 0; + for(i=0;ikind==16){const TableMessageEntry * key; + if(!table_message_ref(&s->input,&key,1)||!key||!key->id||!table_retain_id(&s->output,key->id)||!table_retain_message_frame(s,&elem,depth+1))return 0; + }else if(!table_retain_message_payload(s,&elem,depth))return 0; + }return 1; + } + case 15:case 30:return table_retain_message_payload(s,e,depth); + case 32:return 1; + case 12:case 31:case 33:return table_retain_message_opaque(s,e,0); + case 0:case 17:return 0; + default:return table_retain_message_scalar(s,e); + } +} +static SCHEMA_UNUSED int table_retain_message_payload(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{ + switch(e->kind){ + case 0:case 17:return 0; + case 32:return table_retain_leb(&s->output,0); + case 30:{const TableMessageEntry * entry; + if(!table_message_ref(&s->input,&entry,1))return 0; + return table_retain_id(&s->output,entry?entry->id:0); + } + case 15:{const TableMessageEntry * arm; + if(!table_message_ref(&s->input,&arm,0))return 0; + if(!arm)return table_retain_id(&s->output,0); + return arm->id&&arm->kind&&table_retain_id(&s->output,arm->id)&&table_retain_raw(&s->output,&arm->kind,1)&&table_retain_message_frame(s,arm,depth+1); + } + case 13:case 14:case 16:return table_retain_message_frame(s,e,depth+1); + case 12:case 31:case 33:return table_retain_message_opaque(s,e,1); + default:return table_retain_message_scalar(s,e); + } +} +static SCHEMA_UNUSED int table_retain_message_capture(TableMessageReader * r,const TableMessageEntry * entry,TableRetainWalk keep) +{ + TableRetainMessage s; + uint8_t * record; + s.input=*r; + if(!table_message_skip(r,entry))return 0; + if(!keep.retain)return 1; + record=table_retain_begin(keep,entry->id,entry->kind,&s.output); + if(!record||!table_retain_message_payload(&s,entry,0)||s.input.bits.offset!=r->bits.offset){r->report->retain_lost++; + return 1; + } + table_retain_commit(keep,record,&s.output,r->report); + return 1; +} + +typedef int (*TableRetainSaveNode)(TableWriter *,const void *,uint64_t,TableRetainWalk); +static SCHEMA_UNUSED int table_retain_records(TableWriter * w,TableRetainWalk retention,TableRetainSaveNode save) +{ + uint64_t i; + const TableNumbering * n=w->nodes; + table_writer_leb(w,n->count-1); + for(i=1;icount;i++){const TableNodeEntry * e=n->entries+i; + retention.path=table_retain_root(e->value,0); + table_retain_writer_id(w,e->type->id,retention); + if(!w->buffer){int64_t begin=w->offset; + if(!save(w,e->value,e->type->id,retention))return 0; + table_writer_leb(w,(uint64_t)(w->offset-begin)); + } + else {TableWriter probe=table_retain_probe(w,retention); + if(!save(&probe,e->value,e->type->id,retention))return 0; + table_retain_rewind(&probe,retention); + table_writer_leb(w,(uint64_t)probe.offset); + if(!save(w,e->value,e->type->id,retention))return 0; + }} + return !w->overflow; +} +static SCHEMA_UNUSED int64_t table_retain_graph_save(const void * root,const TableNodeType * type,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator,TableRetainSaveNode save) +{ + TableNumbering numbering; + TableRetainIds ids; + TableRetainWalk retention; + TableWriter w; + int64_t result=-1; + memset(&ids,0,sizeof(ids)); + ids.retain=retain; + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.ids=&ids; + retention.path=table_retain_root(root,1); + if(retain)retain->id_used=0; + if(buffer)table_retain_clear_placed(retain); + w=table_writer_make(buffer,capacity,&ids.known); + if(!table_number_build(&numbering,NULL,root,type,allocator)){table_number_dispose(&numbering); + return -1; + }w.nodes=&numbering; + table_writer_put8(&w,1); + if(!save(&w,root,type->id,retention))goto done; + w.offset--; + if(numbering.count>1){table_retain_writer_id(&w,UINT64_MAX,retention); + table_writer_put8(&w,12); + if(!buffer){int64_t begin=w.offset; + if(!table_retain_records(&w,retention,save))goto done; + table_writer_leb(&w,(uint64_t)(w.offset-begin)); + } + else {TableWriter probe=table_retain_probe(&w,retention); + if(!table_retain_records(&probe,retention,save))goto done; + table_retain_rewind(&probe,retention); + table_writer_leb(&w,(uint64_t)probe.offset); + if(!table_retain_records(&w,retention,save))goto done; + }} + table_writer_put8(&w,0); + table_retain_finish(&w,&ids); + if(!w.overflow){result=w.offset; + if(buffer)table_retain_count_lost(retain,report); + } + done:table_number_dispose(&numbering); + return result; +} + +#endif + +/* table Meta — TABLE-wire storage: relocatable, bounded. C has no member + initializers, so the declared defaults live in meta_reset and nowhere + else — one definition of what a default is (docs/SPEC-TABLES.md) */ +typedef struct Meta { + int32_t build; + char tag[8 + 1]; /* string(8): N + 1 for the terminator the wire does not carry */ + int32_t tag_length; +} Meta; + +/* table Settings — TABLE-wire storage: relocatable, bounded. C has no member + initializers, so the declared defaults live in settings_reset and nowhere + else — one definition of what a default is (docs/SPEC-TABLES.md) */ +typedef struct Settings { + int32_t quality; + char label[16 + 1]; /* string(16): N + 1 for the terminator the wire does not carry */ + int32_t label_length; +} Settings; + +/* table ListNode — TABLE-wire storage: relocatable, bounded. C has no member + initializers, so the declared defaults live in list_node_reset and nowhere else — one definition of what a default is (docs/SPEC-TABLES.md) */ typedef struct ListNode { int32_t value; @@ -1239,115 +3281,250 @@ static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int meta_load_body( TableRead static SCHEMA_UNUSED int64_t settings_measure( const Settings * value ); static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int settings_save_body( TableWriter * w, const Settings * value ); static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int settings_load_body( TableReader * r, Settings * value ); -static SCHEMA_UNUSED int64_t list_node_measure_body( const TableCtx * ctx, const ListNode * value, int32_t depth ); -static SCHEMA_UNUSED int list_node_save_body( const TableCtx * ctx, TableWriter * w, const ListNode * value, int32_t depth ); -static SCHEMA_UNUSED int list_node_load_body( TableReader * r, TableSink * sink, ListNode * value, int32_t depth ); -static SCHEMA_UNUSED int64_t tree_node_measure_body( const TableCtx * ctx, const TreeNode * value, int32_t depth ); -static SCHEMA_UNUSED int tree_node_save_body( const TableCtx * ctx, TableWriter * w, const TreeNode * value, int32_t depth ); -static SCHEMA_UNUSED int tree_node_load_body( TableReader * r, TableSink * sink, TreeNode * value, int32_t depth ); -static SCHEMA_UNUSED int64_t layer_measure_body( const TableCtx * ctx, const Layer * value, int32_t depth ); -static SCHEMA_UNUSED int layer_save_body( const TableCtx * ctx, TableWriter * w, const Layer * value, int32_t depth ); -static SCHEMA_UNUSED int layer_load_body( TableReader * r, TableSink * sink, Layer * value, int32_t depth ); -static SCHEMA_UNUSED int64_t scene_measure_body( const TableCtx * ctx, const Scene * value, int32_t depth ); -static SCHEMA_UNUSED int scene_save_body( const TableCtx * ctx, TableWriter * w, const Scene * value, int32_t depth ); -static SCHEMA_UNUSED int scene_load_body( TableReader * r, TableSink * sink, Scene * value, int32_t depth ); -static SCHEMA_UNUSED int64_t depot_measure_body( const TableCtx * ctx, const Depot * value, int32_t depth ); -static SCHEMA_UNUSED int depot_save_body( const TableCtx * ctx, TableWriter * w, const Depot * value, int32_t depth ); -static SCHEMA_UNUSED int depot_load_body( TableReader * r, TableSink * sink, Depot * value, int32_t depth ); -static SCHEMA_UNUSED int64_t album_measure_body( const TableCtx * ctx, const Album * value, int32_t depth ); -static SCHEMA_UNUSED int album_save_body( const TableCtx * ctx, TableWriter * w, const Album * value, int32_t depth ); -static SCHEMA_UNUSED int album_load_body( TableReader * r, TableSink * sink, Album * value, int32_t depth ); - -/* ---- pointer-graph walkers: pack (Lock), size (Load) ---- */ - -static SCHEMA_UNUSED int64_t settings_pack_measure( const TableCtx * ctx, const Settings * value, int32_t depth ); -static SCHEMA_UNUSED int settings_pack( const TableCtx * ctx, const Settings * src, Settings * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t settings_load_measure_body( TableReader * r, int32_t depth ); -static SCHEMA_UNUSED int64_t list_node_pack_measure( const TableCtx * ctx, const ListNode * value, int32_t depth ); -static SCHEMA_UNUSED int list_node_pack( const TableCtx * ctx, const ListNode * src, ListNode * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t list_node_load_measure_body( TableReader * r, int32_t depth ); -static SCHEMA_UNUSED int64_t tree_node_pack_measure( const TableCtx * ctx, const TreeNode * value, int32_t depth ); -static SCHEMA_UNUSED int tree_node_pack( const TableCtx * ctx, const TreeNode * src, TreeNode * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t tree_node_load_measure_body( TableReader * r, int32_t depth ); -static SCHEMA_UNUSED int64_t layer_pack_measure( const TableCtx * ctx, const Layer * value, int32_t depth ); -static SCHEMA_UNUSED int layer_pack( const TableCtx * ctx, const Layer * src, Layer * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t layer_load_measure_body( TableReader * r, int32_t depth ); -static SCHEMA_UNUSED int64_t scene_pack_measure( const TableCtx * ctx, const Scene * value, int32_t depth ); -static SCHEMA_UNUSED int scene_pack( const TableCtx * ctx, const Scene * src, Scene * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t scene_load_measure_body( TableReader * r, int32_t depth ); -static SCHEMA_UNUSED int64_t depot_pack_measure( const TableCtx * ctx, const Depot * value, int32_t depth ); -static SCHEMA_UNUSED int depot_pack( const TableCtx * ctx, const Depot * src, Depot * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t depot_load_measure_body( TableReader * r, int32_t depth ); -static SCHEMA_UNUSED int64_t album_pack_measure( const TableCtx * ctx, const Album * value, int32_t depth ); -static SCHEMA_UNUSED int album_pack( const TableCtx * ctx, const Album * src, Album * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t album_load_measure_body( TableReader * r, int32_t depth ); - -static SCHEMA_UNUSED int64_t meta_measure( const Meta * value ) -{ - int64_t bytes = 2; /* terminator */ - if ( value->build != 1 ) { bytes += 3 + 4; } /* build */ - if ( value->tag_length < 0 || value->tag_length > 8 ) { return -1; } /* storage invariant */ - if ( value->tag_length > 0 ) { bytes += 3 + 4 + value->tag_length; } /* tag */ - return bytes; +static SCHEMA_UNUSED int list_node_save_body( TableWriter * w, const ListNode * value ); +static SCHEMA_UNUSED int list_node_load_body( TableReader * r, ListNode * value ); +static SCHEMA_UNUSED int tree_node_save_body( TableWriter * w, const TreeNode * value ); +static SCHEMA_UNUSED int tree_node_load_body( TableReader * r, TreeNode * value ); +static SCHEMA_UNUSED int layer_save_body( TableWriter * w, const Layer * value ); +static SCHEMA_UNUSED int layer_load_body( TableReader * r, Layer * value ); +static SCHEMA_UNUSED int scene_save_body( TableWriter * w, const Scene * value ); +static SCHEMA_UNUSED int scene_load_body( TableReader * r, Scene * value ); +static SCHEMA_UNUSED int depot_save_body( TableWriter * w, const Depot * value ); +static SCHEMA_UNUSED int depot_load_body( TableReader * r, Depot * value ); +static SCHEMA_UNUSED int album_save_body( TableWriter * w, const Album * value ); +static SCHEMA_UNUSED int album_load_body( TableReader * r, Album * value ); + +static SCHEMA_UNUSED int schema_graphdemo_meta_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_settings_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_list_node_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_tree_node_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_layer_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_scene_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_depot_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_album_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_settings_arena_alignment_, SCHEMA_TABLE_ALIGNOF(Settings) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_settings_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_settings_graph_edges_( TableNumbering * numbering, const void * storage ); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_list_node_arena_alignment_, SCHEMA_TABLE_ALIGNOF(ListNode) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_list_node_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_list_node_graph_edges_( TableNumbering * numbering, const void * storage ); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_tree_node_arena_alignment_, SCHEMA_TABLE_ALIGNOF(TreeNode) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_tree_node_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_tree_node_graph_edges_( TableNumbering * numbering, const void * storage ); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_layer_arena_alignment_, SCHEMA_TABLE_ALIGNOF(Layer) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_layer_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_layer_graph_edges_( TableNumbering * numbering, const void * storage ); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_scene_arena_alignment_, SCHEMA_TABLE_ALIGNOF(Scene) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_scene_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_scene_graph_edges_( TableNumbering * numbering, const void * storage ); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_depot_arena_alignment_, SCHEMA_TABLE_ALIGNOF(Depot) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_depot_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_depot_graph_edges_( TableNumbering * numbering, const void * storage ); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_album_arena_alignment_, SCHEMA_TABLE_ALIGNOF(Album) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_album_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_album_graph_edges_( TableNumbering * numbering, const void * storage ); +static SCHEMA_UNUSED int schema_graphdemo_settings_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_settings_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_settings_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int schema_graphdemo_list_node_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_list_node_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_list_node_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int schema_graphdemo_tree_node_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_tree_node_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_tree_node_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int schema_graphdemo_layer_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_layer_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_layer_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int schema_graphdemo_scene_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_scene_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_scene_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int schema_graphdemo_depot_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_depot_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_depot_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int schema_graphdemo_album_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_album_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_album_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int meta_save_message_body(TableBitWriter *,const Meta *); +static SCHEMA_UNUSED int settings_save_message_body(TableBitWriter *,const Settings *); +static SCHEMA_UNUSED int list_node_save_message_body(TableBitWriter *,const ListNode *); +static SCHEMA_UNUSED int tree_node_save_message_body(TableBitWriter *,const TreeNode *); +static SCHEMA_UNUSED int layer_save_message_body(TableBitWriter *,const Layer *); +static SCHEMA_UNUSED int scene_save_message_body(TableBitWriter *,const Scene *); +static SCHEMA_UNUSED int depot_save_message_body(TableBitWriter *,const Depot *); +static SCHEMA_UNUSED int album_save_message_body(TableBitWriter *,const Album *); +static SCHEMA_UNUSED int meta_load_message_body(TableMessageReader *,Meta *); +static SCHEMA_UNUSED int settings_load_message_body(TableMessageReader *,Settings *); +static SCHEMA_UNUSED int list_node_load_message_body(TableMessageReader *,ListNode *); +static SCHEMA_UNUSED int tree_node_load_message_body(TableMessageReader *,TreeNode *); +static SCHEMA_UNUSED int layer_load_message_body(TableMessageReader *,Layer *); +static SCHEMA_UNUSED int scene_load_message_body(TableMessageReader *,Scene *); +static SCHEMA_UNUSED int depot_load_message_body(TableMessageReader *,Depot *); +static SCHEMA_UNUSED int album_load_message_body(TableMessageReader *,Album *); +static SCHEMA_UNUSED int schema_graphdemo_meta_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_settings_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_list_node_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_tree_node_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_layer_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_scene_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_depot_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_album_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_meta_wire_tag_( TableWriter * w, const Meta * value ) +{ + if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; } + table_writer_raw( w, value->tag, value->tag_length ); + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int meta_save_body( TableWriter * w, const Meta * value ) +static SCHEMA_UNUSED int meta_save_body( TableWriter * w, const Meta * value ) { - if ( value->build != 1 ) - { - table_writer_put16( w, 0x3138 ); table_writer_put8( w, 4 ); /* build */ - table_writer_put32( w, (uint32_t) ( value->build ) ); + (void) value; + { /* build */ + if ( !( value->build == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x802517e298c70b03ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->build ) ); + } } - if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; } /* storage invariant */ - if ( value->tag_length > 0 ) - { - table_writer_put16( w, 0xbc64 ); table_writer_put8( w, 12 ); /* tag */ - table_writer_put32( w, (uint32_t) value->tag_length ); - table_writer_raw( w, value->tag, value->tag_length ); + { /* tag */ + if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; } + if ( value->tag_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x56d7ab194448a4f3ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_meta_wire_tag_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_meta_wire_tag_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_meta_wire_tag_( w, value ) ) { return 0; } + } + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t meta_measure( const Meta * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !meta_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t meta_save( const Meta * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !meta_save_body( &w, value ) ) { return -1; } - return w.offset; /* == meta_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int meta_load_body( TableReader * r, Meta * value ) +static SCHEMA_UNUSED int meta_load_body( TableReader * r, Meta * value ) { - meta_reset( value ); /* prefill declared defaults in place, then overlay */ + meta_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x3138: /* build */ + case 0x802517e298c70b03ull: /* build */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->build = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->build = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->build = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } - value->build = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->build = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->build = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->build = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xbc64: /* tag */ + case 0x56d7ab194448a4f3ull: /* tag */ { if ( kind != 12 ) { @@ -1355,104 +3532,343 @@ static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int meta_load_body( TableRead if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 8 ) { keep = 8; r->report->clamped++; } - memcpy( value->tag, r->buffer + r->offset, keep ); - value->tag[keep] = 0; - value->tag_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->tag[0] = 0; value->tag_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 8 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 8 ); r->report->clamped++; } + memcpy( value->tag, sub.buffer, (size_t) keep ); value->tag[keep] = 0; value->tag_length = (int32_t) keep; break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int meta_load( Meta * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + meta_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return meta_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t settings_measure( const Settings * value ) -{ - int64_t bytes = 2; /* terminator */ - if ( value->quality != 2 ) { bytes += 3 + 4; } /* quality */ - if ( value->label_length < 0 || value->label_length > 16 ) { return -1; } /* storage invariant */ - if ( value->label_length > 0 ) { bytes += 3 + 4 + value->label_length; } /* label */ - return bytes; +static SCHEMA_UNUSED int meta_save_message_body(TableBitWriter * w,const Meta * value) +{ + (void)value; + { /* build */ + if(!(value->build == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,18,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->build)-UINT64_C(0),10); + } + } + { /* tag */ + if(value->tag_length<0 || value->tag_length>8) return 0; + if(value->tag_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,19,kTableMessageRefBitsHere); + if(value->tag_length<0 || value->tag_length>8) return 0; + table_bit_put(w,(uint64_t)value->tag_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->tag,value->tag_length); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t meta_measure_messages(const Meta * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x802517e298c70b03): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>1000ll){decoded=1000ll; + r->report->clamped++; + } + value->build=(int32_t)decoded; + } + break; + } + case UINT64_C(0x56d7ab194448a4f3): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,8); + memcpy(value->tag,text,(size_t)kept); + value->tag[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->tag_length=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int meta_load_messages(Meta * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_graphdemo_settings_wire_label_( TableWriter * w, const Settings * value ) +{ + if ( value->label_length < 0 || value->label_length > 16 ) { return 0; } + table_writer_raw( w, value->label, value->label_length ); + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int settings_save_body( TableWriter * w, const Settings * value ) +static SCHEMA_UNUSED int settings_save_body( TableWriter * w, const Settings * value ) { - if ( value->quality != 2 ) - { - table_writer_put16( w, 0xcaf3 ); table_writer_put8( w, 4 ); /* quality */ - table_writer_put32( w, (uint32_t) ( value->quality ) ); + (void) value; + { /* quality */ + if ( !( value->quality == 2 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7a8060916400fe66ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->quality ) ); + } } - if ( value->label_length < 0 || value->label_length > 16 ) { return 0; } /* storage invariant */ - if ( value->label_length > 0 ) - { - table_writer_put16( w, 0xe16a ); table_writer_put8( w, 12 ); /* label */ - table_writer_put32( w, (uint32_t) value->label_length ); - table_writer_raw( w, value->label, value->label_length ); + { /* label */ + if ( value->label_length < 0 || value->label_length > 16 ) { return 0; } + if ( value->label_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x39f7fcec8fcb623dull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_settings_wire_label_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_settings_wire_label_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_settings_wire_label_( w, value ) ) { return 0; } + } + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t settings_measure( const Settings * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !settings_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t settings_save( const Settings * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !settings_save_body( &w, value ) ) { return -1; } - return w.offset; /* == settings_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int settings_load_body( TableReader * r, Settings * value ) +static SCHEMA_UNUSED int settings_load_body( TableReader * r, Settings * value ) { - settings_reset( value ); /* prefill declared defaults in place, then overlay */ + settings_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xcaf3: /* quality */ + case 0x7a8060916400fe66ull: /* quality */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 4ll ) { decoded_wide = 4ll; r->report->clamped++; } + value->quality = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 4ll ) { decoded_wide = 4ll; r->report->clamped++; } + value->quality = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 4 ) { decoded_v = 4; r->report->clamped++; } + value->quality = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 4 ) { decoded_v = 4; r->report->clamped++; } - value->quality = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 4ll ) { decoded_wide = 4ll; r->report->clamped++; } + value->quality = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 4ll ) { decoded_wide = 4ll; r->report->clamped++; } + value->quality = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 4 ) { decoded_v = 4; r->report->clamped++; } + value->quality = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xe16a: /* label */ + case 0x39f7fcec8fcb623dull: /* label */ { if ( kind != 12 ) { @@ -1460,119 +3876,321 @@ static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int settings_load_body( Table if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 16 ) { keep = 16; r->report->clamped++; } - memcpy( value->label, r->buffer + r->offset, keep ); - value->label[keep] = 0; - value->label_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->label[0] = 0; value->label_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 16 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 16 ); r->report->clamped++; } + memcpy( value->label, sub.buffer, (size_t) keep ); value->label[keep] = 0; value->label_length = (int32_t) keep; break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int settings_load( Settings * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + settings_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return settings_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t list_node_measure_body( const TableCtx * ctx, const ListNode * value, int32_t depth ) -{ - int64_t bytes = 2; /* terminator */ - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - if ( value->value != 0 ) { bytes += 3 + 4; } /* value */ - if ( value->name_length < 0 || value->name_length > 12 ) { return -1; } /* storage invariant */ - if ( value->name_length > 0 ) { bytes += 3 + 4 + value->name_length; } /* name */ - { - const ListNode * pointee_next = list_node_at( ctx, &value->next ); /* *ListNode */ - if ( pointee_next != NULL ) - { - int64_t body_next = list_node_measure_body( ctx, pointee_next, depth + 1 ); - if ( body_next < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_next; - } - } - return bytes; +static SCHEMA_UNUSED int settings_save_message_body(TableBitWriter * w,const Settings * value) +{ + (void)value; + { /* quality */ + if(!(value->quality == 2)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,28,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->quality)-UINT64_C(0),3); + } + } + { /* label */ + if(value->label_length<0 || value->label_length>16) return 0; + if(value->label_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,29,kTableMessageRefBitsHere); + if(value->label_length<0 || value->label_length>16) return 0; + table_bit_put(w,(uint64_t)value->label_length,5); + table_bit_align_write(w); + table_bit_putbytes(w,value->label,value->label_length); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t settings_measure_messages(const Settings * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x7a8060916400fe66): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>4ll){decoded=4ll; + r->report->clamped++; + } + value->quality=(int32_t)decoded; + } + break; + } + case UINT64_C(0x39f7fcec8fcb623d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,16); + memcpy(value->label,text,(size_t)kept); + value->label[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->label_length=(int32_t)kept; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int settings_load_messages(Settings * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_wire_name_( TableWriter * w, const ListNode * value ) +{ + if ( value->name_length < 0 || value->name_length > 12 ) { return 0; } + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; } -static SCHEMA_UNUSED int list_node_save_body( const TableCtx * ctx, TableWriter * w, const ListNode * value, int32_t depth ) +static SCHEMA_UNUSED int list_node_save_body( TableWriter * w, const ListNode * value ) { - if ( depth > kTableMaxDepth ) { return 0; } /* a data cycle, or a chain past the cap */ - if ( value->value != 0 ) - { - table_writer_put16( w, 0x9194 ); table_writer_put8( w, 4 ); /* value */ - table_writer_put32( w, (uint32_t) ( value->value ) ); + (void) value; + { /* value */ + if ( !( value->value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x7ce4fd9430e80ceaull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->value ) ); + } } - if ( value->name_length < 0 || value->name_length > 12 ) { return 0; } /* storage invariant */ - if ( value->name_length > 0 ) - { - table_writer_put16( w, 0x30df ); table_writer_put8( w, 12 ); /* name */ - table_writer_put32( w, (uint32_t) value->name_length ); - table_writer_raw( w, value->name, value->name_length ); + { /* name */ + if ( value->name_length < 0 || value->name_length > 12 ) { return 0; } + if ( value->name_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc4bcadba8e631b86ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_list_node_wire_name_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_list_node_wire_name_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_list_node_wire_name_( w, value ) ) { return 0; } + } + } } - { - const ListNode * pointee_next = list_node_at( ctx, &value->next ); /* *ListNode */ - if ( pointee_next != NULL ) + { /* next */ + if ( !( value->next.value == 0 ) ) { - int64_t body_next = list_node_measure_body( ctx, pointee_next, depth + 1 ); - if ( body_next < 0 ) { return 0; } - table_writer_put16( w, 0xd15e ); table_writer_put8( w, 13 ); /* next — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_next ); - if ( !list_node_save_body( ctx, w, pointee_next, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xe5316cbaa025f028ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->next); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } -static SCHEMA_UNUSED int list_node_load_body( TableReader * r, TableSink * sink, ListNode * value, int32_t depth ) +static SCHEMA_UNUSED int list_node_load_body( TableReader * r, ListNode * value ) { - list_node_reset( value ); /* prefill declared defaults in place, then overlay */ + list_node_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x9194: /* value */ + case 0x7ce4fd9430e80ceaull: /* value */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->value = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->value = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + value->value = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - value->value = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->value = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->value = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + value->value = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x30df: /* name */ + case 0xc4bcadba8e631b86ull: /* name */ { if ( kind != 12 ) { @@ -1580,146 +4198,228 @@ static SCHEMA_UNUSED int list_node_load_body( TableReader * r, TableSink * sink, if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 12 ) { keep = 12; r->report->clamped++; } - memcpy( value->name, r->buffer + r->offset, keep ); - value->name[keep] = 0; - value->name_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->name[0] = 0; value->name_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 12 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 12 ); r->report->clamped++; } + memcpy( value->name, sub.buffer, (size_t) keep ); value->name[keep] = 0; value->name_length = (int32_t) keep; break; } - case 0xd15e: /* next */ + case 0xe5316cbaa025f028ull: /* next */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - ListNode * pointee = list_node_emplace( sink, &value->next ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - list_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->next,index,UINT64_C(0xf60ec899a5a69fa9),r->report); } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } -static SCHEMA_UNUSED int64_t tree_node_measure_body( const TableCtx * ctx, const TreeNode * value, int32_t depth ) -{ - int64_t bytes = 2; /* terminator */ - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - if ( value->label_length < 0 || value->label_length > 12 ) { return -1; } /* storage invariant */ - if ( value->label_length > 0 ) { bytes += 3 + 4 + value->label_length; } /* label */ - { - const TreeNode * pointee_left = tree_node_at( ctx, &value->left ); /* *TreeNode */ - if ( pointee_left != NULL ) - { - int64_t body_left = tree_node_measure_body( ctx, pointee_left, depth + 1 ); - if ( body_left < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_left; - } - } - { - const TreeNode * pointee_right = tree_node_at( ctx, &value->right ); /* *TreeNode */ - if ( pointee_right != NULL ) - { - int64_t body_right = tree_node_measure_body( ctx, pointee_right, depth + 1 ); - if ( body_right < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_right; - } - } - return bytes; +static SCHEMA_UNUSED int list_node_save_message_body(TableBitWriter * w,const ListNode * value) +{ + (void)value; + { /* value */ + if(!(value->value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,14,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->value)-UINT64_C(0),32); + } + } + { /* name */ + if(value->name_length<0 || value->name_length>12) return 0; + if(value->name_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,10,kTableMessageRefBitsHere); + if(value->name_length<0 || value->name_length>12) return 0; + table_bit_put(w,(uint64_t)value->name_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->name,value->name_length); + } + } + { /* next */ + if(!(value->next.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,15,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->next); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int list_node_load_message_body(TableMessageReader * r,ListNode * value) +{ + list_node_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x7ce4fd9430e80cea): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2147483648ll){decoded=-2147483648ll; + r->report->clamped++; + } + if(decoded>2147483647ll){decoded=2147483647ll; + r->report->clamped++; + } + value->value=(int32_t)decoded; + } + break; + } + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,12); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xe5316cbaa025f028): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->next,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_message_extent_(TableMessageReader * r,int64_t * at) +{ + (void)at; + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + switch(entry->id){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_wire_label_( TableWriter * w, const TreeNode * value ) +{ + if ( value->label_length < 0 || value->label_length > 12 ) { return 0; } + table_writer_raw( w, value->label, value->label_length ); + return !w->overflow; } -static SCHEMA_UNUSED int tree_node_save_body( const TableCtx * ctx, TableWriter * w, const TreeNode * value, int32_t depth ) +static SCHEMA_UNUSED int tree_node_save_body( TableWriter * w, const TreeNode * value ) { - if ( depth > kTableMaxDepth ) { return 0; } /* a data cycle, or a chain past the cap */ - if ( value->label_length < 0 || value->label_length > 12 ) { return 0; } /* storage invariant */ - if ( value->label_length > 0 ) - { - table_writer_put16( w, 0xe16a ); table_writer_put8( w, 12 ); /* label */ - table_writer_put32( w, (uint32_t) value->label_length ); - table_writer_raw( w, value->label, value->label_length ); + (void) value; + { /* label */ + if ( value->label_length < 0 || value->label_length > 12 ) { return 0; } + if ( value->label_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x39f7fcec8fcb623dull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_tree_node_wire_label_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_tree_node_wire_label_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_tree_node_wire_label_( w, value ) ) { return 0; } + } + } } - { - const TreeNode * pointee_left = tree_node_at( ctx, &value->left ); /* *TreeNode */ - if ( pointee_left != NULL ) + { /* left */ + if ( !( value->left.value == 0 ) ) { - int64_t body_left = tree_node_measure_body( ctx, pointee_left, depth + 1 ); - if ( body_left < 0 ) { return 0; } - table_writer_put16( w, 0xfe3a ); table_writer_put8( w, 13 ); /* left — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_left ); - if ( !tree_node_save_body( ctx, w, pointee_left, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x24b070ada2041cb0ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->left); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - { - const TreeNode * pointee_right = tree_node_at( ctx, &value->right ); /* *TreeNode */ - if ( pointee_right != NULL ) + { /* right */ + if ( !( value->right.value == 0 ) ) { - int64_t body_right = tree_node_measure_body( ctx, pointee_right, depth + 1 ); - if ( body_right < 0 ) { return 0; } - table_writer_put16( w, 0x5506 ); table_writer_put8( w, 13 ); /* right — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_right ); - if ( !tree_node_save_body( ctx, w, pointee_right, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x76aaaa535714d805ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->right); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } -static SCHEMA_UNUSED int tree_node_load_body( TableReader * r, TableSink * sink, TreeNode * value, int32_t depth ) +static SCHEMA_UNUSED int tree_node_load_body( TableReader * r, TreeNode * value ) { - tree_node_reset( value ); /* prefill declared defaults in place, then overlay */ + tree_node_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xe16a: /* label */ + case 0x39f7fcec8fcb623dull: /* label */ { if ( kind != 12 ) { @@ -1727,410 +4427,580 @@ static SCHEMA_UNUSED int tree_node_load_body( TableReader * r, TableSink * sink, if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 12 ) { keep = 12; r->report->clamped++; } - memcpy( value->label, r->buffer + r->offset, keep ); - value->label[keep] = 0; - value->label_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->label[0] = 0; value->label_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 12 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 12 ); r->report->clamped++; } + memcpy( value->label, sub.buffer, (size_t) keep ); value->label[keep] = 0; value->label_length = (int32_t) keep; break; } - case 0xfe3a: /* left */ + case 0x24b070ada2041cb0ull: /* left */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - TreeNode * pointee = tree_node_emplace( sink, &value->left ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - tree_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->left,index,UINT64_C(0xb97e90a3784c431d),r->report); } break; } - case 0x5506: /* right */ + case 0x76aaaa535714d805ull: /* right */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - TreeNode * pointee = tree_node_emplace( sink, &value->right ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - tree_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->right,index,UINT64_C(0xb97e90a3784c431d),r->report); } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } -static SCHEMA_UNUSED int64_t layer_measure_body( const TableCtx * ctx, const Layer * value, int32_t depth ) -{ - int64_t bytes = 2; /* terminator */ - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - if ( value->depth != 0 ) { bytes += 3 + 4; } /* depth */ - { - const ListNode * pointee_head = list_node_at( ctx, &value->head ); /* *ListNode */ - if ( pointee_head != NULL ) +static SCHEMA_UNUSED int tree_node_save_message_body(TableBitWriter * w,const TreeNode * value) +{ + (void)value; + { /* label */ + if(value->label_length<0 || value->label_length>12) return 0; + if(value->label_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,32,kTableMessageRefBitsHere); + if(value->label_length<0 || value->label_length>12) return 0; + table_bit_put(w,(uint64_t)value->label_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->label,value->label_length); + } + } + { /* left */ + if(!(value->left.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,33,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->left); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + { /* right */ + if(!(value->right.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,34,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->right); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int tree_node_load_message_body(TableMessageReader * r,TreeNode * value) +{ + tree_node_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x39f7fcec8fcb623d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,12); + memcpy(value->label,text,(size_t)kept); + value->label[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->label_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x24b070ada2041cb0): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->left,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + case UINT64_C(0x76aaaa535714d805): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->right,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_message_extent_(TableMessageReader * r,int64_t * at) +{ + (void)at; + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + switch(entry->id){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int layer_save_body( TableWriter * w, const Layer * value ) +{ + (void) value; + { /* depth */ + if ( !( value->depth == 0 ) ) { - int64_t body_head = list_node_measure_body( ctx, pointee_head, depth + 1 ); - if ( body_head < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_head; + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x75d8e97600b296eaull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->depth ) ); } } - return bytes; -} - -static SCHEMA_UNUSED int layer_save_body( const TableCtx * ctx, TableWriter * w, const Layer * value, int32_t depth ) -{ - if ( depth > kTableMaxDepth ) { return 0; } /* a data cycle, or a chain past the cap */ - if ( value->depth != 0 ) - { - table_writer_put16( w, 0x609f ); table_writer_put8( w, 4 ); /* depth */ - table_writer_put32( w, (uint32_t) ( value->depth ) ); - } - { - const ListNode * pointee_head = list_node_at( ctx, &value->head ); /* *ListNode */ - if ( pointee_head != NULL ) + { /* head */ + if ( !( value->head.value == 0 ) ) { - int64_t body_head = list_node_measure_body( ctx, pointee_head, depth + 1 ); - if ( body_head < 0 ) { return 0; } - table_writer_put16( w, 0x79aa ); table_writer_put8( w, 13 ); /* head — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_head ); - if ( !list_node_save_body( ctx, w, pointee_head, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0a8f12cc5f9a0c03ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } -static SCHEMA_UNUSED int layer_load_body( TableReader * r, TableSink * sink, Layer * value, int32_t depth ) +static SCHEMA_UNUSED int layer_load_body( TableReader * r, Layer * value ) { - layer_reset( value ); /* prefill declared defaults in place, then overlay */ + layer_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x609f: /* depth */ + case 0x75d8e97600b296eaull: /* depth */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; r->report->clamped++; } + value->depth = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; r->report->clamped++; } + value->depth = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 64 ) { decoded_v = 64; r->report->clamped++; } + value->depth = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 64 ) { decoded_v = 64; r->report->clamped++; } - value->depth = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; r->report->clamped++; } + value->depth = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; r->report->clamped++; } + value->depth = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 64 ) { decoded_v = 64; r->report->clamped++; } + value->depth = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x79aa: /* head */ + case 0x0a8f12cc5f9a0c03ull: /* head */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - ListNode * pointee = list_node_emplace( sink, &value->head ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - list_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } -static SCHEMA_UNUSED int64_t scene_measure_body( const TableCtx * ctx, const Scene * value, int32_t depth ) +static SCHEMA_UNUSED int layer_save_message_body(TableBitWriter * w,const Layer * value) +{ + (void)value; + { /* depth */ + if(!(value->depth == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,13,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->depth)-UINT64_C(0),7); + } + } + { /* head */ + if(!(value->head.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,6,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int layer_load_message_body(TableMessageReader * r,Layer * value) +{ + layer_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x75d8e97600b296ea): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>64ll){decoded=64ll; + r->report->clamped++; + } + value->depth=(int32_t)decoded; + } + break; + } + case UINT64_C(0x0a8f12cc5f9a0c03): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_layer_message_extent_(TableMessageReader * r,int64_t * at) +{ + (void)at; + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + switch(entry->id){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_graphdemo_scene_wire_name_( TableWriter * w, const Scene * value ) +{ + if ( value->name_length < 0 || value->name_length > 24 ) { return 0; } + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_graphdemo_scene_wire_layers_( TableWriter * w, const Scene * value ) { - int64_t bytes = 2; /* terminator */ - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - if ( value->name_length < 0 || value->name_length > 24 ) { return -1; } /* storage invariant */ - if ( value->name_length > 0 ) { bytes += 3 + 4 + value->name_length; } /* name */ - if ( value->version != 1 ) { bytes += 3 + 4; } /* version */ - { - const ListNode * pointee_head = list_node_at( ctx, &value->head ); /* *ListNode */ - if ( pointee_head != NULL ) - { - int64_t body_head = list_node_measure_body( ctx, pointee_head, depth + 1 ); - if ( body_head < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_head; - } - } + if ( value->layers_count < 0 || value->layers_count > 4 ) { return 0; } + int32_t i; + table_writer_put8( w, 13 ); table_writer_leb( w, (uint64_t) value->layers_count ); + for ( i = 0; i < value->layers_count; i++ ) { - const TreeNode * pointee_tree = tree_node_at( ctx, &value->tree ); /* *TreeNode */ - if ( pointee_tree != NULL ) + if ( w->buffer == NULL ) { - int64_t body_tree = tree_node_measure_body( ctx, pointee_tree, depth + 1 ); - if ( body_tree < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_tree; + int64_t frame_begin = w->offset; + if ( !layer_save_body( w, &value->layers[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); } - } - { - const Settings * pointee_settings = settings_at( ctx, &value->settings ); /* *Settings */ - if ( pointee_settings != NULL ) + else { - int64_t body_settings = settings_measure( pointee_settings ); - if ( body_settings < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_settings; + TableWriter probe = table_writer_probe( w ); + if ( !layer_save_body( &probe, &value->layers[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !layer_save_body( w, &value->layers[i] ) ) { return 0; } } } - { - const ListNode * pointee_alias = list_node_at( ctx, &value->alias ); /* *ListNode */ - if ( pointee_alias != NULL ) + return !w->overflow; +} + +static SCHEMA_UNUSED int scene_save_body( TableWriter * w, const Scene * value ) +{ + (void) value; + { /* name */ + if ( value->name_length < 0 || value->name_length > 24 ) { return 0; } + if ( value->name_length != 0 ) { - int64_t body_alias = list_node_measure_body( ctx, pointee_alias, depth + 1 ); - if ( body_alias < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_alias; + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc4bcadba8e631b86ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_scene_wire_name_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_scene_wire_name_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_scene_wire_name_( w, value ) ) { return 0; } + } } } - { - int64_t body_ground = layer_measure_body( ctx, &value->ground, depth ); - if ( body_ground < 0 ) { return -1; } - if ( body_ground > 2 ) { bytes += 3 + 4 + body_ground; } /* ground: all-default nested elides */ - } - if ( value->layers_count < 0 || value->layers_count > 4 ) { return -1; } /* storage invariant */ - if ( value->layers_count > 0 ) - { - int32_t i; - bytes += 3 + 4 + 5; /* layers */ - for ( i = 0; i < value->layers_count; i++ ) + { /* version */ + if ( !( value->version == 1 ) ) { - int64_t elem_layers = layer_measure_body( ctx, &value->layers[i], depth ); - if ( elem_layers < 0 ) { return -1; } - bytes += 4 + elem_layers; + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xbb62c62c9808ea37ull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->version ) ); } } - { - int64_t body_meta = meta_measure( &value->meta ); - if ( body_meta < 0 ) { return -1; } - if ( body_meta > 2 ) { bytes += 3 + 4 + body_meta; } /* meta: all-default nested elides */ - } - return bytes; -} - -static SCHEMA_UNUSED int scene_save_body( const TableCtx * ctx, TableWriter * w, const Scene * value, int32_t depth ) -{ - if ( depth > kTableMaxDepth ) { return 0; } /* a data cycle, or a chain past the cap */ - if ( value->name_length < 0 || value->name_length > 24 ) { return 0; } /* storage invariant */ - if ( value->name_length > 0 ) - { - table_writer_put16( w, 0x30df ); table_writer_put8( w, 12 ); /* name */ - table_writer_put32( w, (uint32_t) value->name_length ); - table_writer_raw( w, value->name, value->name_length ); - } - if ( value->version != 1 ) - { - table_writer_put16( w, 0xe8e6 ); table_writer_put8( w, 4 ); /* version */ - table_writer_put32( w, (uint32_t) ( value->version ) ); - } - { - const ListNode * pointee_head = list_node_at( ctx, &value->head ); /* *ListNode */ - if ( pointee_head != NULL ) + { /* head */ + if ( !( value->head.value == 0 ) ) { - int64_t body_head = list_node_measure_body( ctx, pointee_head, depth + 1 ); - if ( body_head < 0 ) { return 0; } - table_writer_put16( w, 0x79aa ); table_writer_put8( w, 13 ); /* head — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_head ); - if ( !list_node_save_body( ctx, w, pointee_head, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0a8f12cc5f9a0c03ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - { - const TreeNode * pointee_tree = tree_node_at( ctx, &value->tree ); /* *TreeNode */ - if ( pointee_tree != NULL ) + { /* tree */ + if ( !( value->tree.value == 0 ) ) { - int64_t body_tree = tree_node_measure_body( ctx, pointee_tree, depth + 1 ); - if ( body_tree < 0 ) { return 0; } - table_writer_put16( w, 0x595e ); table_writer_put8( w, 13 ); /* tree — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_tree ); - if ( !tree_node_save_body( ctx, w, pointee_tree, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x5b25b8ef511eb395ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->tree); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - { - const Settings * pointee_settings = settings_at( ctx, &value->settings ); /* *Settings */ - if ( pointee_settings != NULL ) + { /* settings */ + if ( !( value->settings.value == 0 ) ) { - int64_t body_settings = settings_measure( pointee_settings ); - if ( body_settings < 0 ) { return 0; } - table_writer_put16( w, 0x130e ); table_writer_put8( w, 13 ); /* settings — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_settings ); - if ( !settings_save_body( w, pointee_settings ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xee5f6d7b48b44de8ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->settings); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - { - const ListNode * pointee_alias = list_node_at( ctx, &value->alias ); /* *ListNode */ - if ( pointee_alias != NULL ) + { /* alias */ + if ( !( value->alias.value == 0 ) ) { - int64_t body_alias = list_node_measure_body( ctx, pointee_alias, depth + 1 ); - if ( body_alias < 0 ) { return 0; } - table_writer_put16( w, 0xfc71 ); table_writer_put8( w, 13 ); /* alias — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_alias ); - if ( !list_node_save_body( ctx, w, pointee_alias, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x509220bb65a646b7ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->alias); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - { - int64_t body_ground = layer_measure_body( ctx, &value->ground, depth ); - if ( body_ground < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_ground > 2 ) /* all-default nested elides */ + { /* ground */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !layer_save_body( &default_probe, &value->ground ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0x5bdf ); table_writer_put8( w, 13 ); /* ground */ - table_writer_put32( w, (uint32_t) body_ground ); - if ( !layer_save_body( ctx, w, &value->ground, depth ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x60839e2395be697eull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !layer_save_body( w, &value->ground ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !layer_save_body( &probe, &value->ground ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !layer_save_body( w, &value->ground ) ) { return 0; } + } } } - if ( value->layers_count < 0 || value->layers_count > 4 ) { return 0; } /* storage invariant */ - if ( value->layers_count > 0 ) - { - int64_t len_at_layers; - int32_t i; - table_writer_put16( w, 0x1ee8 ); table_writer_put8( w, 14 ); /* layers */ - len_at_layers = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, (uint32_t) value->layers_count ); - for ( i = 0; i < value->layers_count; i++ ) + { /* layers */ + if ( value->layers_count < 0 || value->layers_count > 4 ) { return 0; } + if ( value->layers_count > 0 ) { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4554e34a747022dfull ); table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at = w->offset; - table_writer_put32( w, 0 ); - if ( !layer_save_body( ctx, w, &value->layers[i], depth ) ) { return 0; } - table_writer_patch32( w, elem_len_at, (uint32_t) ( w->offset - elem_len_at - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_scene_wire_layers_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_scene_wire_layers_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_scene_wire_layers_( w, value ) ) { return 0; } } } - table_writer_patch32( w, len_at_layers, (uint32_t) ( w->offset - len_at_layers - 4 ) ); } - { - int64_t body_meta = meta_measure( &value->meta ); - if ( body_meta < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_meta > 2 ) /* all-default nested elides */ + { /* meta */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !meta_save_body( &default_probe, &value->meta ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0xcea6 ); table_writer_put8( w, 13 ); /* meta */ - table_writer_put32( w, (uint32_t) body_meta ); - if ( !meta_save_body( w, &value->meta ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4320e9a2e32eac38ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !meta_save_body( w, &value->meta ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !meta_save_body( &probe, &value->meta ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !meta_save_body( w, &value->meta ) ) { return 0; } + } } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } -static SCHEMA_UNUSED int scene_load_body( TableReader * r, TableSink * sink, Scene * value, int32_t depth ) +static SCHEMA_UNUSED int scene_load_body( TableReader * r, Scene * value ) { - scene_reset( value ); /* prefill declared defaults in place, then overlay */ + scene_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x30df: /* name */ + case 0xc4bcadba8e631b86ull: /* name */ { if ( kind != 12 ) { @@ -2138,177 +5008,147 @@ static SCHEMA_UNUSED int scene_load_body( TableReader * r, TableSink * sink, Sce if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 24 ) { keep = 24; r->report->clamped++; } - memcpy( value->name, r->buffer + r->offset, keep ); - value->name[keep] = 0; - value->name_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->name[0] = 0; value->name_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 24 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 24 ); r->report->clamped++; } + memcpy( value->name, sub.buffer, (size_t) keep ); value->name[keep] = 0; value->name_length = (int32_t) keep; break; } - case 0xe8e6: /* version */ + case 0xbb62c62c9808ea37ull: /* version */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 99ll ) { decoded_wide = 99ll; r->report->clamped++; } + value->version = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 99ll ) { decoded_wide = 99ll; r->report->clamped++; } + value->version = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 99 ) { decoded_v = 99; r->report->clamped++; } + value->version = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 99 ) { decoded_v = 99; r->report->clamped++; } - value->version = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 99ll ) { decoded_wide = 99ll; r->report->clamped++; } + value->version = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 99ll ) { decoded_wide = 99ll; r->report->clamped++; } + value->version = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 99 ) { decoded_v = 99; r->report->clamped++; } + value->version = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0x79aa: /* head */ + case 0x0a8f12cc5f9a0c03ull: /* head */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - ListNode * pointee = list_node_emplace( sink, &value->head ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - list_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); } break; } - case 0x595e: /* tree */ + case 0x5b25b8ef511eb395ull: /* tree */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - TreeNode * pointee = tree_node_emplace( sink, &value->tree ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - tree_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->tree,index,UINT64_C(0xb97e90a3784c431d),r->report); } break; } - case 0x130e: /* settings */ + case 0xee5f6d7b48b44de8ull: /* settings */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - Settings * pointee = settings_emplace( sink, &value->settings ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - settings_load_body( &sub, pointee ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->settings,index,UINT64_C(0x9d8b8aa2b404c2c8),r->report); } break; } - case 0xfc71: /* alias */ + case 0x509220bb65a646b7ull: /* alias */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - ListNode * pointee = list_node_emplace( sink, &value->alias ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - list_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->alias,index,UINT64_C(0xf60ec899a5a69fa9),r->report); } break; } - case 0x5bdf: /* ground */ + case 0x60839e2395be697eull: /* ground */ { if ( kind != 13 ) { @@ -2316,18 +5156,13 @@ static SCHEMA_UNUSED int scene_load_body( TableReader * r, TableSink * sink, Sce if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - layer_load_body( &sub, sink, &value->ground, depth ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + layer_load_body( &sub, &value->ground ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; layer_reset( &value->ground ); } break; } - case 0x1ee8: /* layers */ + case 0x4554e34a747022dfull: /* layers */ { if ( kind != 14 ) { @@ -2335,47 +5170,39 @@ static SCHEMA_UNUSED int scene_load_body( TableReader * r, TableSink * sink, Sce if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - uint32_t keep; - TableReader sub; - uint32_t i; - uint32_t decoded = 0; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - keep = count; - if ( keep > 4 ) { keep = 4; r->report->clamped++; } - /* elements are BOUNDED by the field body: a count the length - cannot cover keeps the decoded prefix, flags malformed, and - the parent continues at the next field — following fields' - bytes are never fabricated into elements */ - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < keep; i++ ) + uint8_t elem_kind; uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; } + else { - uint32_t elem_len; - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) + { + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } + r->report->widened++; + } + kept = count; if ( kept > 4 ) { kept = 4; r->report->clamped++; } + int32_t previous_count = value->layers_count; + value->layers_count = 0; + for ( i = 0; i < kept; i++ ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - layer_load_body( &elem, sink, &value->layers[i], depth ); + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; goto end_layers; } + layer_load_body( &elem, &value->layers[i] ); + if(elem.offset!=elem.size) { r->report->malformed=1; layer_reset(&value->layers[i]); } + value->layers_count = (int32_t) i + 1; } - sub.offset += elem_len; - decoded = i + 1; + end_layers: ; + { uint32_t tail; for (tail=(uint32_t)value->layers_count;tail<(uint32_t)previous_count && tail<4;tail++) { + layer_reset(&value->layers[tail]); + } } } - value->layers_count = (int32_t) decoded; } - r->offset = body_end; /* excess elements and slack skip via the length */ break; } - case 0xcea6: /* meta */ + case 0x4320e9a2e32eac38ull: /* meta */ { if ( kind != 13 ) { @@ -2383,176 +5210,454 @@ static SCHEMA_UNUSED int scene_load_body( TableReader * r, TableSink * sink, Sce if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - meta_load_body( &sub, &value->meta ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + meta_load_body( &sub, &value->meta ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; meta_reset( &value->meta ); } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } -static SCHEMA_UNUSED int64_t depot_measure_body( const TableCtx * ctx, const Depot * value, int32_t depth ) +static SCHEMA_UNUSED int scene_save_message_body(TableBitWriter * w,const Scene * value) +{ + (void)value; + { /* name */ + if(value->name_length<0 || value->name_length>24) return 0; + if(value->name_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,20,kTableMessageRefBitsHere); + if(value->name_length<0 || value->name_length>24) return 0; + table_bit_put(w,(uint64_t)value->name_length,5); + table_bit_align_write(w); + table_bit_putbytes(w,value->name,value->name_length); + } + } + { /* version */ + if(!(value->version == 1)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,21,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->version)-UINT64_C(0),7); + } + } + { /* head */ + if(!(value->head.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,6,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + { /* tree */ + if(!(value->tree.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,22,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->tree); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + { /* settings */ + if(!(value->settings.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,23,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->settings); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + { /* alias */ + if(!(value->alias.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,24,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->alias); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + { /* ground */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!layer_save_message_body(&probe,&value->ground)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,25,kTableMessageRefBitsHere); + if(!layer_save_message_body(w,&value->ground)) return 0; + } + } + { /* layers */ + if(value->layers_count<0 || value->layers_count>4) return 0; + if(value->layers_count>0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,26,kTableMessageRefBitsHere); + if(value->layers_count<0 || value->layers_count>4) return 0; + table_bit_put(w,(uint64_t)value->layers_count-0,3); + { int32_t i; + for(i=0;ilayers_count;i++) { + if(!layer_save_message_body(w,&value->layers[i])) return 0; + } } + } + } + { /* meta */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!meta_save_message_body(&probe,&value->meta)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,27,kTableMessageRefBitsHere); + if(!meta_save_message_body(w,&value->meta)) return 0; + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int scene_load_message_body(TableMessageReader * r,Scene * value) +{ + scene_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,24); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xbb62c62c9808ea37): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>99ll){decoded=99ll; + r->report->clamped++; + } + value->version=(int32_t)decoded; + } + break; + } + case UINT64_C(0x0a8f12cc5f9a0c03): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + case UINT64_C(0x5b25b8ef511eb395): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->tree,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + case UINT64_C(0xee5f6d7b48b44de8): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->settings,index,UINT64_C(0x9d8b8aa2b404c2c8),r->report); + } + break; + } + case UINT64_C(0x509220bb65a646b7): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->alias,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + case UINT64_C(0x60839e2395be697e): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!layer_load_message_body(r,&value->ground))return 0; + break; + } + case UINT64_C(0x4554e34a747022df): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ilayers[i] : &scratch; + if(!layer_load_message_body(r,&(*item)))return 0; + }if(walkvalue_bits))goto malformed; + value->layers_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x4320e9a2e32eac38): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!meta_load_message_body(r,&value->meta))return 0; + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_scene_message_extent_(TableMessageReader * r,int64_t * at) +{ + (void)at; + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + switch(entry->id){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +#define SCHEMA_GRAPHDEMO_DEPOT_BANKS_AT(value,key) SCHEMA_TABLE_KEYED_AT((value).banks,(key),TIER_MAX) +static SCHEMA_UNUSED int schema_graphdemo_depot_wire_name_( TableWriter * w, const Depot * value ) +{ + if ( value->name_length < 0 || value->name_length > 12 ) { return 0; } + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_graphdemo_depot_wire_banks_( TableWriter * w, const Depot * value ) { - int64_t bytes = 2; /* terminator */ - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - if ( value->name_length < 0 || value->name_length > 12 ) { return -1; } /* storage invariant */ - if ( value->name_length > 0 ) { bytes += 3 + 4 + value->name_length; } /* name */ - { - int64_t pairs_banks = 0, body_banks = 0; - int32_t i; - for ( i = 0; i < TIER_MAX; i++ ) /* [Tier]: every stored slot is a named variant's */ - { - int64_t elem_bytes = layer_measure_body( ctx, &value->banks[i], depth ); - if ( elem_bytes < 0 ) { return -1; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Tier) ( i + 1 ) ) /* Tier rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TIER_NONE: key_id = 0; break; - case TIER_LOW: key_id = 0x24fc; break; - case TIER_HIGH: key_id = 0xb817; break; - default: return -1; /* no variant names this value: no wire identity */ - } - (void) key_id; - pairs_banks++; body_banks += 2 + 4 + elem_bytes; /* key, length, body */ - } - if ( pairs_banks > 0 ) { bytes += 3 + 4 + 5 + body_banks; } /* banks */ - } - if ( value->spare_present ) /* ?Meta: presence decides, not content */ + uint64_t count = 0; int32_t i; + for ( i = 0; i < TIER_MAX; i++ ) { - int64_t body_spare = meta_measure( &value->spare ); - if ( body_spare < 0 ) { return -1; } - bytes += 3 + 4 + body_spare; /* spare */ + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !layer_save_body( &slot_probe, &value->banks[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + count++; } + table_writer_put8( w, 13 ); table_writer_leb( w, count ); + for ( i = 0; i < TIER_MAX; i++ ) { - const ListNode * pointee_head = list_node_at( ctx, &value->head ); /* *ListNode */ - if ( pointee_head != NULL ) + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !layer_save_body( &slot_probe, &value->banks[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + switch ( i + 1 ) + { + case TIER_NONE: table_writer_leb( w, 0 ); break; + case TIER_LOW: table_writer_id( w, 0x24f3a319b88552c1ull ); break; + case TIER_HIGH: table_writer_id( w, 0x9deeefd89ca8a81dull ); break; + default: return 0; /* no declared identity */ + } + if ( w->buffer == NULL ) { - int64_t body_head = list_node_measure_body( ctx, pointee_head, depth + 1 ); - if ( body_head < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_head; + int64_t frame_begin = w->offset; + if ( !layer_save_body( w, &value->banks[i] ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !layer_save_body( &probe, &value->banks[i] ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !layer_save_body( w, &value->banks[i] ) ) { return 0; } } } - return bytes; + return !w->overflow; } -static SCHEMA_UNUSED int depot_save_body( const TableCtx * ctx, TableWriter * w, const Depot * value, int32_t depth ) +static SCHEMA_UNUSED int depot_save_body( TableWriter * w, const Depot * value ) { - if ( depth > kTableMaxDepth ) { return 0; } /* a data cycle, or a chain past the cap */ - if ( value->name_length < 0 || value->name_length > 12 ) { return 0; } /* storage invariant */ - if ( value->name_length > 0 ) - { - table_writer_put16( w, 0x30df ); table_writer_put8( w, 12 ); /* name */ - table_writer_put32( w, (uint32_t) value->name_length ); - table_writer_raw( w, value->name, value->name_length ); - } - { - uint32_t pairs_banks = 0; - int32_t i; - for ( i = 0; i < TIER_MAX; i++ ) /* [Tier]: every stored slot is a named variant's */ + (void) value; + { /* name */ + if ( value->name_length < 0 || value->name_length > 12 ) { return 0; } + if ( value->name_length != 0 ) { - int64_t elem_bytes = layer_measure_body( ctx, &value->banks[i], depth ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Tier) ( i + 1 ) ) /* Tier rides as the u16 hash of its VARIANT NAME (§5) */ + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc4bcadba8e631b86ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_depot_wire_name_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else { - case TIER_NONE: key_id = 0; break; - case TIER_LOW: key_id = 0x24fc; break; - case TIER_HIGH: key_id = 0xb817; break; - default: return 0; /* no variant names this value: no wire identity */ + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_depot_wire_name_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_depot_wire_name_( w, value ) ) { return 0; } } - (void) key_id; - pairs_banks++; } - if ( pairs_banks > 0 ) + } + { /* banks */ + int rides = 0; int32_t i; + for ( i = 0; i < TIER_MAX; i++ ) + { + TableWriter slot_probe = table_writer_probe( w ); slot_probe.check_default = 1; + if ( !layer_save_body( &slot_probe, &value->banks[i] ) ) { return 0; } + table_writer_rewind(&slot_probe); + if ( slot_probe.offset == 1 ) { continue; } + rides = 1; break; + } + if ( rides ) { - int64_t len_at_banks; - /* KIND 16, not 14: a keyed body and a positional one are - incompatible, so a reader of the other kind must see a kind - mismatch and skip, never misdecode (docs/SPEC-TABLES.md §3.2) */ - table_writer_put16( w, 0x7f34 ); table_writer_put8( w, 16 ); /* banks (keyed by Tier) */ - len_at_banks = w->offset; table_writer_put32( w, 0 ); - table_writer_put8( w, 13 ); table_writer_put32( w, pairs_banks ); - /* ASCENDING BY VARIANT ORDINAL, which is slot order — this - writer's choice, and a reader must not rely on it: every - slot is found by its key (docs/SPEC-TABLES.md §3.2) */ - for ( i = 0; i < TIER_MAX; i++ ) + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xdd9eb981e152e90cull ); table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) { - int64_t elem_len_at_banks; - int64_t elem_bytes = layer_measure_body( ctx, &value->banks[i], depth ); - if ( elem_bytes < 0 ) { return 0; } - if ( elem_bytes <= 2 ) { continue; } /* an all-default slot elides */ - uint16_t key_id = 0; - switch ( (Tier) ( i + 1 ) ) /* Tier rides as the u16 hash of its VARIANT NAME (§5) */ - { - case TIER_NONE: key_id = 0; break; - case TIER_LOW: key_id = 0x24fc; break; - case TIER_HIGH: key_id = 0xb817; break; - default: return 0; /* no variant names this value: no wire identity */ - } - (void) key_id; - table_writer_put16( w, key_id ); /* the slot's VARIANT id, not its position */ - elem_len_at_banks = w->offset; table_writer_put32( w, 0 ); - if ( !layer_save_body( ctx, w, &value->banks[i], depth ) ) { return 0; } - table_writer_patch32( w, elem_len_at_banks, (uint32_t) ( w->offset - elem_len_at_banks - 4 ) ); + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_depot_wire_banks_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_depot_wire_banks_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_depot_wire_banks_( w, value ) ) { return 0; } } - table_writer_patch32( w, len_at_banks, (uint32_t) ( w->offset - len_at_banks - 4 ) ); } } - if ( value->spare_present ) /* ?Meta */ - { - int64_t body_spare = meta_measure( &value->spare ); - if ( body_spare < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - table_writer_put16( w, 0x3a4f ); table_writer_put8( w, 13 ); /* spare */ - table_writer_put32( w, (uint32_t) body_spare ); - if ( !meta_save_body( w, &value->spare ) ) { return 0; } + { /* spare */ + if ( value->spare_present ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x4339ee8ab21c8380ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !meta_save_body( w, &value->spare ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !meta_save_body( &probe, &value->spare ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !meta_save_body( w, &value->spare ) ) { return 0; } + } + } } - { - const ListNode * pointee_head = list_node_at( ctx, &value->head ); /* *ListNode */ - if ( pointee_head != NULL ) + { /* head */ + if ( !( value->head.value == 0 ) ) { - int64_t body_head = list_node_measure_body( ctx, pointee_head, depth + 1 ); - if ( body_head < 0 ) { return 0; } - table_writer_put16( w, 0x79aa ); table_writer_put8( w, 13 ); /* head — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_head ); - if ( !list_node_save_body( ctx, w, pointee_head, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0a8f12cc5f9a0c03ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } -static SCHEMA_UNUSED int depot_load_body( TableReader * r, TableSink * sink, Depot * value, int32_t depth ) +static SCHEMA_UNUSED int depot_load_body( TableReader * r, Depot * value ) { - depot_reset( value ); /* prefill declared defaults in place, then overlay */ + depot_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x30df: /* name */ + case 0xc4bcadba8e631b86ull: /* name */ { if ( kind != 12 ) { @@ -2560,20 +5665,18 @@ static SCHEMA_UNUSED int depot_load_body( TableReader * r, TableSink * sink, Dep if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 12 ) { keep = 12; r->report->clamped++; } - memcpy( value->name, r->buffer + r->offset, keep ); - value->name[keep] = 0; - value->name_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->name[0] = 0; value->name_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 12 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 12 ); r->report->clamped++; } + memcpy( value->name, sub.buffer, (size_t) keep ); value->name[keep] = 0; value->name_length = (int32_t) keep; break; } - case 0x7f34: /* banks */ + case 0xdd9eb981e152e90cull: /* banks */ { if ( kind != 16 ) { @@ -2581,67 +5684,32 @@ static SCHEMA_UNUSED int depot_load_body( TableReader * r, TableSink * sink, Dep if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - int64_t body_end; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( sub.size >= 2 ) { - uint8_t elem_kind = table_reader_get8( r ); - uint32_t count = table_reader_get32( r ); - TableReader sub; - uint32_t i; - if ( elem_kind != 13 ) { r->report->kind_mismatch++; r->offset = body_end; break; } - sub = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); + uint8_t elem_kind; uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; break; } + if ( elem_kind != 13 ) { if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; break; } r->report->widened++; } for ( i = 0; i < count; i++ ) { - uint16_t key; - uint32_t elem_len; - Tier slot; - if ( !table_reader_has( &sub, 2 ) ) { r->report->malformed = 1; break; } - key = table_reader_get16( &sub ); - if ( !table_reader_has( &sub, 4 ) ) { r->report->malformed = 1; break; } - elem_len = table_reader_get32( &sub ); - if ( !table_reader_has( &sub, elem_len ) ) { r->report->malformed = 1; break; } - if ( key == 0 ) - { - /* None is the NULL KEY: 0 is the reserved id no declared - name can fold to, so a body carrying one is DAMAGED, not - merely foreign. Framing damage stops this body, keeps what - it decoded, and the parent reads on past the length - (docs/SPEC-TABLES.md §3.2, §4). */ - r->report->malformed = 1; - break; - } - slot = TIER_NONE; - { - int32_t before = r->report->unknown; - switch ( key ) - { - case 0: slot = TIER_NONE; break; - case 0x24fc: slot = TIER_LOW; break; - case 0xb817: slot = TIER_HIGH; break; - default: slot = TIER_NONE; r->report->unknown++; break; /* an id this build cannot name */ - } - if ( r->report->unknown != before ) - { - sub.offset += elem_len; /* a slot this reader cannot name */ - continue; - } - } + uint64_t key_ref, key; int32_t slot = -1; TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; break; } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) { - TableReader elem = table_reader_make( sub.buffer + sub.offset, elem_len, r->report ); - layer_load_body( &elem, sink, &value->banks[(int32_t) slot - 1], depth ); + case 0x24f3a319b88552c1ull: slot = 0; break; + case 0x9deeefd89ca8a81dull: slot = 1; break; + default: r->report->unknown++; break; } - sub.offset += elem_len; + if ( slot < 0 ) { continue; } + layer_load_body( &elem, &value->banks[slot] ); + if(elem.offset!=elem.size) { r->report->malformed=1; layer_reset(&value->banks[slot]); } } } - r->offset = body_end; /* unread pairs and slack skip via the length */ break; } - case 0x3a4f: /* spare */ + case 0x4339ee8ab21c8380ull: /* spare */ { if ( kind != 13 ) { @@ -2649,190 +5717,366 @@ static SCHEMA_UNUSED int depot_load_body( TableReader * r, TableSink * sink, Dep if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - meta_load_body( &sub, &value->spare ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + meta_load_body( &sub, &value->spare ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; meta_reset( &value->spare ); } value->spare_present = 1; break; } - case 0x79aa: /* head */ + case 0x0a8f12cc5f9a0c03ull: /* head */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - ListNode * pointee = list_node_emplace( sink, &value->head ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - list_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } -static SCHEMA_UNUSED int64_t album_measure_body( const TableCtx * ctx, const Album * value, int32_t depth ) -{ - int64_t bytes = 2; /* terminator */ - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - if ( value->name_length < 0 || value->name_length > 16 ) { return -1; } /* storage invariant */ - if ( value->name_length > 0 ) { bytes += 3 + 4 + value->name_length; } /* name */ - { - int64_t body_tint = colour_measure( &value->tint ); - if ( body_tint < 0 ) { return -1; } - if ( body_tint > 2 ) { bytes += 3 + 4 + body_tint; } /* tint: all-default nested elides */ +static SCHEMA_UNUSED int depot_save_message_body(TableBitWriter * w,const Depot * value) +{ + (void)value; + { /* name */ + if(value->name_length<0 || value->name_length>12) return 0; + if(value->name_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,10,kTableMessageRefBitsHere); + if(value->name_length<0 || value->name_length>12) return 0; + table_bit_put(w,(uint64_t)value->name_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->name,value->name_length); + } + } + { /* banks */ + int rides=0; + { int32_t i; + for(i=0;i<2;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!layer_save_message_body(&probe,&value->banks[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + rides=1; + break; + } } + if(rides) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,11,kTableMessageRefBitsHere); + { uint32_t count=0; + int32_t i; + for(i=0;i<2;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!layer_save_message_body(&probe,&value->banks[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + count++; } - { - int64_t body_stamp = stamp_measure( &value->stamp ); - if ( body_stamp < 0 ) { return -1; } - if ( body_stamp > 2 ) { bytes += 3 + 4 + body_stamp; } /* stamp: all-default nested elides */ - } - { - int64_t body_marker = marker_measure_body( ctx, &value->marker, depth ); - if ( body_marker < 0 ) { return -1; } - if ( body_marker > 2 ) { bytes += 3 + 4 + body_marker; } /* marker: all-default nested elides */ + table_bit_put(w,count,2); + for(i=0;i<2;i++) { + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!layer_save_message_body(&probe,&value->banks[i])) return 0; + if(probe.bits==kTableMessageRefBitsHere) continue; + switch(i+1) { + case TIER_NONE: table_bit_put(w,0,kTableMessageRefBitsHere); + break; + case TIER_LOW: table_bit_put(w,35,kTableMessageRefBitsHere); + break; + case TIER_HIGH: table_bit_put(w,36,kTableMessageRefBitsHere); + break; + default: return 0; } - { - const Marker * pointee_pin = marker_at( ctx, &value->pin ); /* *Marker */ - if ( pointee_pin != NULL ) + if(!layer_save_message_body(w,&value->banks[i])) return 0; + } } + } + } + { /* spare */ + if(value->spare_present) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,12,kTableMessageRefBitsHere); + if(!meta_save_message_body(w,&value->spare)) return 0; + } + } + { /* head */ + if(!(value->head.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,6,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int depot_load_message_body(TableMessageReader * r,Depot * value) +{ + depot_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,12); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xdd9eb981e152e90c): { + if(!(entry->kind==16 && entry->elem_kind==13)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0x24f3a319b88552c1):slot=0; + break; + case UINT64_C(0x9deeefd89ca8a81d):slot=1; + break; + default:break; + } if(slot<0){r->report->unknown++; + { Layer scratch; + memset(&scratch,0,sizeof(scratch)); + if(!layer_load_message_body(r,&scratch))return 0; + }continue; + } + if(!layer_load_message_body(r,&value->banks[slot]))return 0; + } } + break; + } + case UINT64_C(0x4339ee8ab21c8380): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!meta_load_message_body(r,&value->spare))return 0; + value->spare_present=1; + break; + } + case UINT64_C(0x0a8f12cc5f9a0c03): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_depot_message_extent_(TableMessageReader * r,int64_t * at) +{ + (void)at; + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + switch(entry->id){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_graphdemo_album_wire_name_( TableWriter * w, const Album * value ) +{ + if ( value->name_length < 0 || value->name_length > 16 ) { return 0; } + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int album_save_body( TableWriter * w, const Album * value ) +{ + (void) value; + { /* name */ + if ( value->name_length < 0 || value->name_length > 16 ) { return 0; } + if ( value->name_length != 0 ) { - int64_t body_pin = marker_measure_body( ctx, pointee_pin, depth + 1 ); - if ( body_pin < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_pin; - } - } - { - const ListNode * pointee_head = list_node_at( ctx, &value->head ); /* *ListNode */ - if ( pointee_head != NULL ) - { - int64_t body_head = list_node_measure_body( ctx, pointee_head, depth + 1 ); - if ( body_head < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_head; + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xc4bcadba8e631b86ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_album_wire_name_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_album_wire_name_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_album_wire_name_( w, value ) ) { return 0; } + } } } - return bytes; -} - -static SCHEMA_UNUSED int album_save_body( const TableCtx * ctx, TableWriter * w, const Album * value, int32_t depth ) -{ - if ( depth > kTableMaxDepth ) { return 0; } /* a data cycle, or a chain past the cap */ - if ( value->name_length < 0 || value->name_length > 16 ) { return 0; } /* storage invariant */ - if ( value->name_length > 0 ) - { - table_writer_put16( w, 0x30df ); table_writer_put8( w, 12 ); /* name */ - table_writer_put32( w, (uint32_t) value->name_length ); - table_writer_raw( w, value->name, value->name_length ); - } - { - int64_t body_tint = colour_measure( &value->tint ); - if ( body_tint < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_tint > 2 ) /* all-default nested elides */ + { /* tint */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !colour_save_body( &default_probe, &value->tint ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0x82b9 ); table_writer_put8( w, 13 ); /* tint */ - table_writer_put32( w, (uint32_t) body_tint ); - if ( !colour_save_body( w, &value->tint ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x1e4984ef2e958a4cull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !colour_save_body( w, &value->tint ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !colour_save_body( &probe, &value->tint ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !colour_save_body( w, &value->tint ) ) { return 0; } + } } } - { - int64_t body_stamp = stamp_measure( &value->stamp ); - if ( body_stamp < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_stamp > 2 ) /* all-default nested elides */ + { /* stamp */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !stamp_save_body( &default_probe, &value->stamp ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0x0dc6 ); table_writer_put8( w, 13 ); /* stamp */ - table_writer_put32( w, (uint32_t) body_stamp ); - if ( !stamp_save_body( w, &value->stamp ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xee7ba9ad45c64144ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !stamp_save_body( w, &value->stamp ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !stamp_save_body( &probe, &value->stamp ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !stamp_save_body( w, &value->stamp ) ) { return 0; } + } } } - { - int64_t body_marker = marker_measure_body( ctx, &value->marker, depth ); - if ( body_marker < 0 ) { return 0; } /* storage invariant, refused as measure refuses it */ - if ( body_marker > 2 ) /* all-default nested elides */ + { /* marker */ + TableWriter default_probe = table_writer_probe( w ); default_probe.check_default = 1; + if ( !marker_save_body( &default_probe, &value->marker ) ) { return 0; } + table_writer_rewind(&default_probe); + if ( default_probe.offset > 1 ) { - table_writer_put16( w, 0x866f ); table_writer_put8( w, 13 ); /* marker */ - table_writer_put32( w, (uint32_t) body_marker ); - if ( !marker_save_body( ctx, w, &value->marker, depth ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xeddcb72b15486e77ull ); table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !marker_save_body( w, &value->marker ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !marker_save_body( &probe, &value->marker ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !marker_save_body( w, &value->marker ) ) { return 0; } + } } } - { - const Marker * pointee_pin = marker_at( ctx, &value->pin ); /* *Marker */ - if ( pointee_pin != NULL ) + { /* pin */ + if ( !( value->pin.value == 0 ) ) { - int64_t body_pin = marker_measure_body( ctx, pointee_pin, depth + 1 ); - if ( body_pin < 0 ) { return 0; } - table_writer_put16( w, 0x69d5 ); table_writer_put8( w, 13 ); /* pin — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_pin ); - if ( !marker_save_body( ctx, w, pointee_pin, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x77af761956600b54ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->pin); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - { - const ListNode * pointee_head = list_node_at( ctx, &value->head ); /* *ListNode */ - if ( pointee_head != NULL ) + { /* head */ + if ( !( value->head.value == 0 ) ) { - int64_t body_head = list_node_measure_body( ctx, pointee_head, depth + 1 ); - if ( body_head < 0 ) { return 0; } - table_writer_put16( w, 0x79aa ); table_writer_put8( w, 13 ); /* head — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_head ); - if ( !list_node_save_body( ctx, w, pointee_head, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x0a8f12cc5f9a0c03ull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } -static SCHEMA_UNUSED int album_load_body( TableReader * r, TableSink * sink, Album * value, int32_t depth ) +static SCHEMA_UNUSED int album_load_body( TableReader * r, Album * value ) { - album_reset( value ); /* prefill declared defaults in place, then overlay */ + album_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0x30df: /* name */ + case 0xc4bcadba8e631b86ull: /* name */ { if ( kind != 12 ) { @@ -2840,20 +6084,18 @@ static SCHEMA_UNUSED int album_load_body( TableReader * r, TableSink * sink, Alb if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 16 ) { keep = 16; r->report->clamped++; } - memcpy( value->name, r->buffer + r->offset, keep ); - value->name[keep] = 0; - value->name_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->name[0] = 0; value->name_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 16 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 16 ); r->report->clamped++; } + memcpy( value->name, sub.buffer, (size_t) keep ); value->name[keep] = 0; value->name_length = (int32_t) keep; break; } - case 0x82b9: /* tint */ + case 0x1e4984ef2e958a4cull: /* tint */ { if ( kind != 13 ) { @@ -2861,18 +6103,13 @@ static SCHEMA_UNUSED int album_load_body( TableReader * r, TableSink * sink, Alb if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - colour_load_body( &sub, &value->tint ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + colour_load_body( &sub, &value->tint ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; colour_reset( &value->tint ); } break; } - case 0x0dc6: /* stamp */ + case 0xee7ba9ad45c64144ull: /* stamp */ { if ( kind != 13 ) { @@ -2880,18 +6117,13 @@ static SCHEMA_UNUSED int album_load_body( TableReader * r, TableSink * sink, Alb if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - stamp_load_body( &sub, &value->stamp ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + stamp_load_body( &sub, &value->stamp ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; stamp_reset( &value->stamp ); } break; } - case 0x866f: /* marker */ + case 0xeddcb72b15486e77ull: /* marker */ { if ( kind != 13 ) { @@ -2899,999 +6131,3841 @@ static SCHEMA_UNUSED int album_load_body( TableReader * r, TableSink * sink, Alb if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - marker_load_body( &sub, sink, &value->marker, depth ); - } - r->offset += body_len; + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + marker_load_body( &sub, &value->marker ); + if ( sub.offset != sub.size ) { r->report->malformed = 1; marker_reset( &value->marker ); } break; } - case 0x69d5: /* pin */ + case 0x77af761956600b54ull: /* pin */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - Marker * pointee = marker_emplace( sink, &value->pin ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - marker_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->pin,index,UINT64_C(0xd6458a3eef83d457),r->report); } break; } - case 0x79aa: /* head */ + case 0x0a8f12cc5f9a0c03ull: /* head */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; - break; - } - { - ListNode * pointee = list_node_emplace( sink, &value->head ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - list_node_load_body( &sub, sink, pointee, depth + 1 ); - } - r->offset += body_len; + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } -/* settings_pack_measure: the packed region bytes of everything Settings POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t settings_pack_measure( const TableCtx * ctx, const Settings * value, int32_t depth ) -{ - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - (void) ctx; (void) value; /* no pointers below this node */ - return bytes; -} - -/* settings_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. - - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int settings_pack( const TableCtx * ctx, const Settings * src, Settings * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) -{ - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( Settings ) ); /* a plain struct, by construction */ - (void) ctx; (void) base; (void) capacity; (void) used; - return 1; +static SCHEMA_UNUSED int album_save_message_body(TableBitWriter * w,const Album * value) +{ + (void)value; + { /* name */ + if(value->name_length<0 || value->name_length>16) return 0; + if(value->name_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,1,kTableMessageRefBitsHere); + if(value->name_length<0 || value->name_length>16) return 0; + table_bit_put(w,(uint64_t)value->name_length,5); + table_bit_align_write(w); + table_bit_putbytes(w,value->name,value->name_length); + } + } + { /* tint */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!colour_save_message_body(&probe,&value->tint)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,2,kTableMessageRefBitsHere); + if(!colour_save_message_body(w,&value->tint)) return 0; + } + } + { /* stamp */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!stamp_save_message_body(&probe,&value->stamp)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,3,kTableMessageRefBitsHere); + if(!stamp_save_message_body(w,&value->stamp)) return 0; + } + } + { /* marker */ + TableBitWriter probe=*w; + probe.buffer=NULL; + probe.capacity=INT64_MAX; + probe.bits=0; + probe.check_default=1; + if(!marker_save_message_body(&probe,&value->marker)) return 0; + if(probe.bits>kTableMessageRefBitsHere) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,4,kTableMessageRefBitsHere); + if(!marker_save_message_body(w,&value->marker)) return 0; + } + } + { /* pin */ + if(!(value->pin.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,5,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->pin); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + { /* head */ + if(!(value->head.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,6,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int album_load_message_body(TableMessageReader * r,Album * value) +{ + album_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,16); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x1e4984ef2e958a4c): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!colour_load_message_body(r,&value->tint))return 0; + break; + } + case UINT64_C(0xee7ba9ad45c64144): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!stamp_load_message_body(r,&value->stamp))return 0; + break; + } + case UINT64_C(0xeddcb72b15486e77): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + if(!marker_load_message_body(r,&value->marker))return 0; + break; + } + case UINT64_C(0x77af761956600b54): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->pin,index,UINT64_C(0xd6458a3eef83d457),r->report); + } + break; + } + case UINT64_C(0x0a8f12cc5f9a0c03): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_album_message_extent_(TableMessageReader * r,int64_t * at) +{ + (void)at; + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + switch(entry->id){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int meta_save_body_retain(TableWriter *,const Meta *, TableRetainWalk retention); +static SCHEMA_UNUSED int meta_load_body_retain(TableReader *,Meta *, TableRetainWalk retention); +static SCHEMA_UNUSED int settings_save_body_retain(TableWriter *,const Settings *, TableRetainWalk retention); +static SCHEMA_UNUSED int settings_load_body_retain(TableReader *,Settings *, TableRetainWalk retention); +static SCHEMA_UNUSED int list_node_save_body_retain(TableWriter *,const ListNode *, TableRetainWalk retention); +static SCHEMA_UNUSED int list_node_load_body_retain(TableReader *,ListNode *, TableRetainWalk retention); +static SCHEMA_UNUSED int tree_node_save_body_retain(TableWriter *,const TreeNode *, TableRetainWalk retention); +static SCHEMA_UNUSED int tree_node_load_body_retain(TableReader *,TreeNode *, TableRetainWalk retention); +static SCHEMA_UNUSED int layer_save_body_retain(TableWriter *,const Layer *, TableRetainWalk retention); +static SCHEMA_UNUSED int layer_load_body_retain(TableReader *,Layer *, TableRetainWalk retention); +static SCHEMA_UNUSED int scene_save_body_retain(TableWriter *,const Scene *, TableRetainWalk retention); +static SCHEMA_UNUSED int scene_load_body_retain(TableReader *,Scene *, TableRetainWalk retention); +static SCHEMA_UNUSED int depot_save_body_retain(TableWriter *,const Depot *, TableRetainWalk retention); +static SCHEMA_UNUSED int depot_load_body_retain(TableReader *,Depot *, TableRetainWalk retention); +static SCHEMA_UNUSED int album_save_body_retain(TableWriter *,const Album *, TableRetainWalk retention); +static SCHEMA_UNUSED int album_load_body_retain(TableReader *,Album *, TableRetainWalk retention); +static SCHEMA_UNUSED int meta_load_message_body_retain(TableMessageReader *,Meta *, TableRetainWalk retention); +static SCHEMA_UNUSED int settings_load_message_body_retain(TableMessageReader *,Settings *, TableRetainWalk retention); +static SCHEMA_UNUSED int list_node_load_message_body_retain(TableMessageReader *,ListNode *, TableRetainWalk retention); +static SCHEMA_UNUSED int tree_node_load_message_body_retain(TableMessageReader *,TreeNode *, TableRetainWalk retention); +static SCHEMA_UNUSED int layer_load_message_body_retain(TableMessageReader *,Layer *, TableRetainWalk retention); +static SCHEMA_UNUSED int scene_load_message_body_retain(TableMessageReader *,Scene *, TableRetainWalk retention); +static SCHEMA_UNUSED int depot_load_message_body_retain(TableMessageReader *,Depot *, TableRetainWalk retention); +static SCHEMA_UNUSED int album_load_message_body_retain(TableMessageReader *,Album *, TableRetainWalk retention); +static SCHEMA_UNUSED int schema_graphdemo_meta_wire_tag__retain( TableWriter * w, const Meta * value , TableRetainWalk retention) +{ + (void)retention; + + if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; + } + table_writer_raw( w, value->tag, value->tag_length ); + return !w->overflow; } -/* settings_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t settings_load_measure_body( TableReader * r, int32_t depth ) +static SCHEMA_UNUSED int meta_save_body_retain( TableWriter * w, const Meta * value , TableRetainWalk retention) { - int64_t bytes = 0; - (void) r; (void) depth; /* nothing below this body allocates */ - return bytes; -} + (void)retention; -/* list_node_pack_measure: the packed region bytes of everything ListNode POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t list_node_pack_measure( const TableCtx * ctx, const ListNode * value, int32_t depth ) -{ - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - { - const ListNode * pointee = list_node_at( ctx, &value->next ); /* next */ - if ( pointee != NULL ) + (void) value; + TableRetainWalk body_keep=retention; + { /* build */ + retention=table_retain_step(body_keep,0,0); + if ( !( value->build == 1 ) ) { - int64_t inner = list_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ) + inner; + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x802517e298c70b03ull , retention); + table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->build ) ); } } - return bytes; -} - -/* list_node_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. - - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int list_node_pack( const TableCtx * ctx, const ListNode * src, ListNode * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) -{ - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( ListNode ) ); /* a plain struct, by construction */ - { - const ListNode * pointee; - dst->next.value = 0; /* next */ - pointee = list_node_at( ctx, &src->next ); - if ( pointee != NULL ) + { /* tag */ + retention=table_retain_step(body_keep,1,0); + if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; + } + if ( value->tag_length != 0 ) { - int64_t at = table_align_up64( *used ); - ListNode * child; - if ( at + (int64_t) sizeof( ListNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( ListNode ) ); - child = (ListNode *) (void *) ( base + at ); - dst->next.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->next ); - if ( !list_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x56d7ab194448a4f3ull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_meta_wire_tag__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_meta_wire_tag__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_meta_wire_tag__retain( w, value , retention) ) { return 0; + } + } } } - return 1; + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; } -/* list_node_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t list_node_load_measure_body( TableReader * r, int32_t depth ) +static SCHEMA_UNUSED int meta_load_body_retain( TableReader * r, Meta * value , TableRetainWalk retention) { - int64_t bytes = 0; + (void)retention; + + meta_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); for ( ;; ) { - uint16_t field_id; + uint64_t ref, id; uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { return bytes; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return bytes; } - if ( !table_reader_has( r, 1 ) ) { return bytes; } + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); - switch ( field_id ) + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) { - case 0xd15e: /* next (*ListNode) */ + case 0x802517e298c70b03ull: /* build */ { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; + r->report->clamped++; + } + value->build = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; + r->report->clamped++; + } + value->build = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 1000 ) { decoded_v = 1000; + r->report->clamped++; + } + value->build = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + switch ( kind ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ); - bytes += list_node_load_measure_body( &sub, depth + 1 ); + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; + r->report->clamped++; + } + value->build = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; + r->report->clamped++; + } + value->build = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 1000 ) { decoded_v = 1000; + r->report->clamped++; + } + value->build = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; } - r->offset += body_len; break; } - default: + case 0x56d7ab194448a4f3ull: /* tag */ { - if ( !table_reader_skip( r, kind ) ) { return bytes; } + if ( kind != 12 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; + value->tag[0] = 0; + value->tag_length = 0; + break; + } + keep = (uint64_t) sub.size; + if ( keep > 8 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 8 ); + r->report->clamped++; + } + memcpy( value->tag, sub.buffer, (size_t) keep ); + value->tag[keep] = 0; + value->tag_length = (int32_t) keep; break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } + break; + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; } } } -/* tree_node_pack_measure: the packed region bytes of everything TreeNode POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t tree_node_pack_measure( const TableCtx * ctx, const TreeNode * value, int32_t depth ) -{ - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - { - const TreeNode * pointee = tree_node_at( ctx, &value->left ); /* left */ - if ( pointee != NULL ) - { - int64_t inner = tree_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( TreeNode ) ) + inner; - } - } - { - const TreeNode * pointee = tree_node_at( ctx, &value->right ); /* right */ - if ( pointee != NULL ) - { - int64_t inner = tree_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( TreeNode ) ) + inner; - } +static SCHEMA_UNUSED int meta_load_message_body_retain(TableMessageReader * r,Meta * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + meta_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x802517e298c70b03): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>1000ll){decoded=1000ll; + r->report->clamped++; + } + value->build=(int32_t)decoded; + } + break; + } + case UINT64_C(0x56d7ab194448a4f3): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,8); + memcpy(value->tag,text,(size_t)kept); + value->tag[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->tag_length=(int32_t)kept; + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_settings_wire_label__retain( TableWriter * w, const Settings * value , TableRetainWalk retention) +{ + (void)retention; + + if ( value->label_length < 0 || value->label_length > 16 ) { return 0; } - return bytes; + table_writer_raw( w, value->label, value->label_length ); + return !w->overflow; } -/* tree_node_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. - - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int tree_node_pack( const TableCtx * ctx, const TreeNode * src, TreeNode * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) +static SCHEMA_UNUSED int settings_save_body_retain( TableWriter * w, const Settings * value , TableRetainWalk retention) { - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( TreeNode ) ); /* a plain struct, by construction */ - { - const TreeNode * pointee; - dst->left.value = 0; /* left */ - pointee = tree_node_at( ctx, &src->left ); - if ( pointee != NULL ) + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* quality */ + retention=table_retain_step(body_keep,0,0); + if ( !( value->quality == 2 ) ) { - int64_t at = table_align_up64( *used ); - TreeNode * child; - if ( at + (int64_t) sizeof( TreeNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( TreeNode ) ); - child = (TreeNode *) (void *) ( base + at ); - dst->left.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->left ); - if ( !tree_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x7a8060916400fe66ull , retention); + table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->quality ) ); } } - { - const TreeNode * pointee; - dst->right.value = 0; /* right */ - pointee = tree_node_at( ctx, &src->right ); - if ( pointee != NULL ) + { /* label */ + retention=table_retain_step(body_keep,1,0); + if ( value->label_length < 0 || value->label_length > 16 ) { return 0; + } + if ( value->label_length != 0 ) { - int64_t at = table_align_up64( *used ); - TreeNode * child; - if ( at + (int64_t) sizeof( TreeNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( TreeNode ) ); - child = (TreeNode *) (void *) ( base + at ); - dst->right.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->right ); - if ( !tree_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x39f7fcec8fcb623dull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_settings_wire_label__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_settings_wire_label__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_settings_wire_label__retain( w, value , retention) ) { return 0; + } + } } } - return 1; + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; } -/* tree_node_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t tree_node_load_measure_body( TableReader * r, int32_t depth ) +static SCHEMA_UNUSED int settings_load_body_retain( TableReader * r, Settings * value , TableRetainWalk retention) { - int64_t bytes = 0; + (void)retention; + + settings_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); for ( ;; ) { - uint16_t field_id; + uint64_t ref, id; uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { return bytes; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return bytes; } - if ( !table_reader_has( r, 1 ) ) { return bytes; } + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); - switch ( field_id ) + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) { - case 0xfe3a: /* left (*TreeNode) */ + case 0x7a8060916400fe66ull: /* quality */ { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 4ll ) { decoded_wide = 4ll; + r->report->clamped++; + } + value->quality = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 4ll ) { decoded_wide = 4ll; + r->report->clamped++; + } + value->quality = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 4 ) { decoded_v = 4; + r->report->clamped++; + } + value->quality = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + switch ( kind ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( TreeNode ) ); - bytes += tree_node_load_measure_body( &sub, depth + 1 ); + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 4ll ) { decoded_wide = 4ll; + r->report->clamped++; + } + value->quality = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 4ll ) { decoded_wide = 4ll; + r->report->clamped++; + } + value->quality = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 4 ) { decoded_v = 4; + r->report->clamped++; + } + value->quality = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; } - r->offset += body_len; break; } - case 0x5506: /* right (*TreeNode) */ + case 0x39f7fcec8fcb623dull: /* label */ { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) + if ( kind != 12 ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( TreeNode ) ); - bytes += tree_node_load_measure_body( &sub, depth + 1 ); + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; + value->label[0] = 0; + value->label_length = 0; + break; } - r->offset += body_len; + keep = (uint64_t) sub.size; + if ( keep > 16 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 16 ); + r->report->clamped++; + } + memcpy( value->label, sub.buffer, (size_t) keep ); + value->label[keep] = 0; + value->label_length = (int32_t) keep; break; } - default: - { - if ( !table_reader_skip( r, kind ) ) { return bytes; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } break; - } + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; } } } -/* layer_pack_measure: the packed region bytes of everything Layer POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t layer_pack_measure( const TableCtx * ctx, const Layer * value, int32_t depth ) -{ - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - { - const ListNode * pointee = list_node_at( ctx, &value->head ); /* head */ - if ( pointee != NULL ) - { - int64_t inner = list_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ) + inner; - } +static SCHEMA_UNUSED int settings_load_message_body_retain(TableMessageReader * r,Settings * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + settings_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x7a8060916400fe66): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>4ll){decoded=4ll; + r->report->clamped++; + } + value->quality=(int32_t)decoded; + } + break; + } + case UINT64_C(0x39f7fcec8fcb623d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,16); + memcpy(value->label,text,(size_t)kept); + value->label[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->label_length=(int32_t)kept; + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_wire_name__retain( TableWriter * w, const ListNode * value , TableRetainWalk retention) +{ + (void)retention; + + if ( value->name_length < 0 || value->name_length > 12 ) { return 0; } - return bytes; + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; } -/* layer_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. - - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int layer_pack( const TableCtx * ctx, const Layer * src, Layer * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) +static SCHEMA_UNUSED int list_node_save_body_retain( TableWriter * w, const ListNode * value , TableRetainWalk retention) { - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( Layer ) ); /* a plain struct, by construction */ - { - const ListNode * pointee; - dst->head.value = 0; /* head */ - pointee = list_node_at( ctx, &src->head ); - if ( pointee != NULL ) + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* value */ + retention=table_retain_step(body_keep,0,0); + if ( !( value->value == 0 ) ) { - int64_t at = table_align_up64( *used ); - ListNode * child; - if ( at + (int64_t) sizeof( ListNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( ListNode ) ); - child = (ListNode *) (void *) ( base + at ); - dst->head.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->head ); - if ( !list_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x7ce4fd9430e80ceaull , retention); + table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->value ) ); } } - return 1; + { /* name */ + retention=table_retain_step(body_keep,1,0); + if ( value->name_length < 0 || value->name_length > 12 ) { return 0; + } + if ( value->name_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xc4bcadba8e631b86ull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_list_node_wire_name__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_list_node_wire_name__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_list_node_wire_name__retain( w, value , retention) ) { return 0; + } + } + } + } + { /* next */ + retention=table_retain_step(body_keep,2,0); + if ( !( value->next.value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xe5316cbaa025f028ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->next); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } + } + } + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; } -/* layer_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t layer_load_measure_body( TableReader * r, int32_t depth ) +static SCHEMA_UNUSED int list_node_load_body_retain( TableReader * r, ListNode * value , TableRetainWalk retention) { - int64_t bytes = 0; + (void)retention; + + list_node_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); for ( ;; ) { - uint16_t field_id; + uint64_t ref, id; uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { return bytes; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return bytes; } - if ( !table_reader_has( r, 1 ) ) { return bytes; } + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); - switch ( field_id ) + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) { - case 0x79aa: /* head (*ListNode) */ + case 0x7ce4fd9430e80ceaull: /* value */ { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) + if ( kind != 4 ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ); - bytes += list_node_load_measure_body( &sub, depth + 1 ); + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->value = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->value = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + value->value = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + value->value = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + value->value = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + value->value = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; } - r->offset += body_len; break; } - default: + case 0xc4bcadba8e631b86ull: /* name */ + { + if ( kind != 12 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; + value->name[0] = 0; + value->name_length = 0; + break; + } + keep = (uint64_t) sub.size; + if ( keep > 12 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 12 ); + r->report->clamped++; + } + memcpy( value->name, sub.buffer, (size_t) keep ); + value->name[keep] = 0; + value->name_length = (int32_t) keep; + break; + } + case 0xe5316cbaa025f028ull: /* next */ { - if ( !table_reader_skip( r, kind ) ) { return bytes; } + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,2,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->next,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } + break; + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; } } } -/* scene_pack_measure: the packed region bytes of everything Scene POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t scene_pack_measure( const TableCtx * ctx, const Scene * value, int32_t depth ) +static SCHEMA_UNUSED int list_node_load_message_body_retain(TableMessageReader * r,ListNode * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + list_node_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x7ce4fd9430e80cea): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<-2147483648ll){decoded=-2147483648ll; + r->report->clamped++; + } + if(decoded>2147483647ll){decoded=2147483647ll; + r->report->clamped++; + } + value->value=(int32_t)decoded; + } + break; + } + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,12); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xe5316cbaa025f028): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,2,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->next,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_wire_label__retain( TableWriter * w, const TreeNode * value , TableRetainWalk retention) +{ + (void)retention; + + if ( value->label_length < 0 || value->label_length > 12 ) { return 0; + } + table_writer_raw( w, value->label, value->label_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int tree_node_save_body_retain( TableWriter * w, const TreeNode * value , TableRetainWalk retention) { - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - { - const ListNode * pointee = list_node_at( ctx, &value->head ); /* head */ - if ( pointee != NULL ) + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* label */ + retention=table_retain_step(body_keep,0,0); + if ( value->label_length < 0 || value->label_length > 12 ) { return 0; + } + if ( value->label_length != 0 ) { - int64_t inner = list_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ) + inner; + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x39f7fcec8fcb623dull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_tree_node_wire_label__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_tree_node_wire_label__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_tree_node_wire_label__retain( w, value , retention) ) { return 0; + } + } } } - { - const TreeNode * pointee = tree_node_at( ctx, &value->tree ); /* tree */ - if ( pointee != NULL ) + { /* left */ + retention=table_retain_step(body_keep,1,0); + if ( !( value->left.value == 0 ) ) { - int64_t inner = tree_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( TreeNode ) ) + inner; + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x24b070ada2041cb0ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->left); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } } } - { - const Settings * pointee = settings_at( ctx, &value->settings ); /* settings */ - if ( pointee != NULL ) + { /* right */ + retention=table_retain_step(body_keep,2,0); + if ( !( value->right.value == 0 ) ) { - int64_t inner = settings_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( Settings ) ) + inner; + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x76aaaa535714d805ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->right); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } } } + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int tree_node_load_body_retain( TableReader * r, TreeNode * value , TableRetainWalk retention) +{ + (void)retention; + + tree_node_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + for ( ;; ) { - const ListNode * pointee = list_node_at( ctx, &value->alias ); /* alias */ - if ( pointee != NULL ) + uint64_t ref, id; + uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); + kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) + { + case 0x39f7fcec8fcb623dull: /* label */ + { + if ( kind != 12 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; + value->label[0] = 0; + value->label_length = 0; + break; + } + keep = (uint64_t) sub.size; + if ( keep > 12 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 12 ); + r->report->clamped++; + } + memcpy( value->label, sub.buffer, (size_t) keep ); + value->label[keep] = 0; + value->label_length = (int32_t) keep; + break; + } + case 0x24b070ada2041cb0ull: /* left */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->left,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + case 0x76aaaa535714d805ull: /* right */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,2,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->right,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } + break; + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; + } + } +} + +static SCHEMA_UNUSED int tree_node_load_message_body_retain(TableMessageReader * r,TreeNode * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + tree_node_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x39f7fcec8fcb623d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,12); + memcpy(value->label,text,(size_t)kept); + value->label[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->label_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x24b070ada2041cb0): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->left,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + case UINT64_C(0x76aaaa535714d805): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,2,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->right,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int layer_save_body_retain( TableWriter * w, const Layer * value , TableRetainWalk retention) +{ + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* depth */ + retention=table_retain_step(body_keep,0,0); + if ( !( value->depth == 0 ) ) { - int64_t inner = list_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ) + inner; + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x75d8e97600b296eaull , retention); + table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->depth ) ); } } - { /* ground (nested by value) */ - int64_t inner = layer_pack_measure( ctx, &value->ground, depth ); - if ( inner < 0 ) { return -1; } - bytes += inner; + { /* head */ + retention=table_retain_step(body_keep,1,0); + if ( !( value->head.value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x0a8f12cc5f9a0c03ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } + } } - { int32_t i; for ( i = 0; i < value->layers_count && i < 4; i++ ) /* layers */ + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int layer_load_body_retain( TableReader * r, Layer * value , TableRetainWalk retention) +{ + (void)retention; + + layer_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + for ( ;; ) { - int64_t inner = layer_pack_measure( ctx, &value->layers[i], depth ); - if ( inner < 0 ) { return -1; } - bytes += inner; - } } - return bytes; + uint64_t ref, id; + uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); + kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) + { + case 0x75d8e97600b296eaull: /* depth */ + { + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; + r->report->clamped++; + } + value->depth = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; + r->report->clamped++; + } + value->depth = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 64 ) { decoded_v = 64; + r->report->clamped++; + } + value->depth = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; + r->report->clamped++; + } + value->depth = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 64ll ) { decoded_wide = 64ll; + r->report->clamped++; + } + value->depth = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 64 ) { decoded_v = 64; + r->report->clamped++; + } + value->depth = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + break; + } + case 0x0a8f12cc5f9a0c03ull: /* head */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } + break; + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; + } + } } -/* scene_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. +static SCHEMA_UNUSED int layer_load_message_body_retain(TableMessageReader * r,Layer * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + layer_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x75d8e97600b296ea): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>64ll){decoded=64ll; + r->report->clamped++; + } + value->depth=(int32_t)decoded; + } + break; + } + case UINT64_C(0x0a8f12cc5f9a0c03): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_scene_wire_name__retain( TableWriter * w, const Scene * value , TableRetainWalk retention) +{ + (void)retention; + + if ( value->name_length < 0 || value->name_length > 24 ) { return 0; + } + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; +} - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int scene_pack( const TableCtx * ctx, const Scene * src, Scene * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) +static SCHEMA_UNUSED int schema_graphdemo_scene_wire_layers__retain( TableWriter * w, const Scene * value , TableRetainWalk retention) { - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( Scene ) ); /* a plain struct, by construction */ + (void)retention; + + if ( value->layers_count < 0 || value->layers_count > 4 ) { return 0; + } + int32_t i; + table_writer_put8( w, 13 ); + table_writer_leb( w, (uint64_t) value->layers_count ); + for ( i = 0; i < value->layers_count; i++ ) { - const ListNode * pointee; - dst->head.value = 0; /* head */ - pointee = list_node_at( ctx, &src->head ); - if ( pointee != NULL ) + retention=table_retain_index(retention,(uint32_t)(i)); + if ( w->buffer == NULL ) { - int64_t at = table_align_up64( *used ); - ListNode * child; - if ( at + (int64_t) sizeof( ListNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( ListNode ) ); - child = (ListNode *) (void *) ( base + at ); - dst->head.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->head ); - if ( !list_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + int64_t frame_begin = w->offset; + if ( !layer_save_body_retain( w, &value->layers[i] , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !layer_save_body_retain( &probe, &value->layers[i] , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !layer_save_body_retain( w, &value->layers[i] , retention) ) { return 0; + } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int scene_save_body_retain( TableWriter * w, const Scene * value , TableRetainWalk retention) +{ + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* name */ + retention=table_retain_step(body_keep,0,0); + if ( value->name_length < 0 || value->name_length > 24 ) { return 0; + } + if ( value->name_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xc4bcadba8e631b86ull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_scene_wire_name__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_scene_wire_name__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_scene_wire_name__retain( w, value , retention) ) { return 0; + } + } + } + } + { /* version */ + retention=table_retain_step(body_keep,1,0); + if ( !( value->version == 1 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xbb62c62c9808ea37ull , retention); + table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->version ) ); + } + } + { /* head */ + retention=table_retain_step(body_keep,2,0); + if ( !( value->head.value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x0a8f12cc5f9a0c03ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } + } + } + { /* tree */ + retention=table_retain_step(body_keep,3,0); + if ( !( value->tree.value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x5b25b8ef511eb395ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->tree); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } + } + } + { /* settings */ + retention=table_retain_step(body_keep,4,0); + if ( !( value->settings.value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xee5f6d7b48b44de8ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->settings); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } + } + } + { /* alias */ + retention=table_retain_step(body_keep,5,0); + if ( !( value->alias.value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x509220bb65a646b7ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->alias); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } + } + } + { /* ground */ + retention=table_retain_step(body_keep,6,0); + TableWriter default_probe = table_retain_probe( w , retention); + default_probe.check_default = 1; + if ( !layer_save_body_retain( &default_probe, &value->ground , retention) ) { return 0; + } + table_retain_rewind(&default_probe, retention); + if ( default_probe.offset > 1 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x60839e2395be697eull , retention); + table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !layer_save_body_retain( w, &value->ground , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !layer_save_body_retain( &probe, &value->ground , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !layer_save_body_retain( w, &value->ground , retention) ) { return 0; + } + } + } + } + { /* layers */ + retention=table_retain_step(body_keep,7,0); + if ( value->layers_count < 0 || value->layers_count > 4 ) { return 0; + } + if ( value->layers_count > 0 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x4554e34a747022dfull , retention); + table_writer_put8( w, 14 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_scene_wire_layers__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_scene_wire_layers__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_scene_wire_layers__retain( w, value , retention) ) { return 0; + } + } + } + } + { /* meta */ + retention=table_retain_step(body_keep,8,0); + TableWriter default_probe = table_retain_probe( w , retention); + default_probe.check_default = 1; + if ( !meta_save_body_retain( &default_probe, &value->meta , retention) ) { return 0; + } + table_retain_rewind(&default_probe, retention); + if ( default_probe.offset > 1 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x4320e9a2e32eac38ull , retention); + table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !meta_save_body_retain( w, &value->meta , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !meta_save_body_retain( &probe, &value->meta , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !meta_save_body_retain( w, &value->meta , retention) ) { return 0; + } + } + } + } + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int scene_load_body_retain( TableReader * r, Scene * value , TableRetainWalk retention) +{ + (void)retention; + + scene_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + for ( ;; ) + { + uint64_t ref, id; + uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); + kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) + { + case 0xc4bcadba8e631b86ull: /* name */ + { + if ( kind != 12 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; + value->name[0] = 0; + value->name_length = 0; + break; + } + keep = (uint64_t) sub.size; + if ( keep > 24 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 24 ); + r->report->clamped++; + } + memcpy( value->name, sub.buffer, (size_t) keep ); + value->name[keep] = 0; + value->name_length = (int32_t) keep; + break; + } + case 0xbb62c62c9808ea37ull: /* version */ + { + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 99ll ) { decoded_wide = 99ll; + r->report->clamped++; + } + value->version = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 99ll ) { decoded_wide = 99ll; + r->report->clamped++; + } + value->version = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 99 ) { decoded_v = 99; + r->report->clamped++; + } + value->version = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 99ll ) { decoded_wide = 99ll; + r->report->clamped++; + } + value->version = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 99ll ) { decoded_wide = 99ll; + r->report->clamped++; + } + value->version = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 99 ) { decoded_v = 99; + r->report->clamped++; + } + value->version = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + break; + } + case 0x0a8f12cc5f9a0c03ull: /* head */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,2,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + case 0x5b25b8ef511eb395ull: /* tree */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,3,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->tree,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + case 0xee5f6d7b48b44de8ull: /* settings */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,4,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->settings,index,UINT64_C(0x9d8b8aa2b404c2c8),r->report); + } + break; + } + case 0x509220bb65a646b7ull: /* alias */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,5,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->alias,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + case 0x60839e2395be697eull: /* ground */ + { + if ( kind != 13 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,6,0); + table_retain_discard(body_keep,1,6); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + layer_load_body_retain( &sub, &value->ground , retention); + if ( sub.offset != sub.size ) { r->report->malformed = 1; + layer_reset( &value->ground ); + } + break; + } + case 0x4554e34a747022dfull: /* layers */ + { + if ( kind != 14 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,7,0); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; + uint64_t count, kept, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; + } + else + { + if ( elem_kind != 13 ) + { + if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; + break; + } + r->report->widened++; + } + {TableRetainWalk parent_keep=retention; + uint32_t ordinal=parent_keep.path.steps[--parent_keep.path.depth].ordinal; + table_retain_discard(parent_keep,1,ordinal); + } + kept = count; + if ( kept > 4 ) { kept = 4; + r->report->clamped++; + } + int32_t previous_count = value->layers_count; + value->layers_count = 0; + for ( i = 0; i < kept; i++ ) + { + retention=table_retain_index(retention,(uint32_t)(i)); + TableReader elem; + if ( !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; + goto end_layers; + } + layer_load_body_retain( &elem, &value->layers[i] , retention); + if(elem.offset!=elem.size) { r->report->malformed=1; + layer_reset(&value->layers[i]); + } + value->layers_count = (int32_t) i + 1; + } + end_layers: ; + { uint32_t tail; + for (tail=(uint32_t)value->layers_count;tail<(uint32_t)previous_count && tail<4;tail++) { + layer_reset(&value->layers[tail]); + } } + } + } + break; + } + case 0x4320e9a2e32eac38ull: /* meta */ + { + if ( kind != 13 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,8,0); + table_retain_discard(body_keep,1,8); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + meta_load_body_retain( &sub, &value->meta , retention); + if ( sub.offset != sub.size ) { r->report->malformed = 1; + meta_reset( &value->meta ); + } + break; + } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } + break; + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; + } + } +} + +static SCHEMA_UNUSED int scene_load_message_body_retain(TableMessageReader * r,Scene * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + scene_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,24); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xbb62c62c9808ea37): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>99ll){decoded=99ll; + r->report->clamped++; + } + value->version=(int32_t)decoded; + } + break; + } + case UINT64_C(0x0a8f12cc5f9a0c03): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,2,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + case UINT64_C(0x5b25b8ef511eb395): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,3,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->tree,index,UINT64_C(0xb97e90a3784c431d),r->report); + } + break; + } + case UINT64_C(0xee5f6d7b48b44de8): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,4,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->settings,index,UINT64_C(0x9d8b8aa2b404c2c8),r->report); + } + break; + } + case UINT64_C(0x509220bb65a646b7): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,5,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->alias,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + case UINT64_C(0x60839e2395be697e): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,6,0); + table_retain_discard(body_keep,1,6); + if(!layer_load_message_body_retain(r,&value->ground, retention))return 0; + break; + } + case UINT64_C(0x4554e34a747022df): { + if(!(entry->kind==14 && entry->elem_kind==13)){if(entry->kind==14 && table_kind_widens(entry->elem_kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,7,0); + {uint64_t n,kept,walk,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + kept=n>4?4:n; + if(keptreport->clamped++; + {TableRetainWalk parent_keep=retention; + uint32_t ordinal=parent_keep.path.steps[--parent_keep.path.depth].ordinal; + table_retain_discard(parent_keep,1,ordinal); + } + walk=element_entry->value_bits>=0?kept:n; + for(i=0;ilayers[i] : &scratch; + retention=table_retain_index(retention,(uint32_t)(i)); + if(!layer_load_message_body_retain(r,&(*item), retention))return 0; + }if(walkvalue_bits))goto malformed; + value->layers_count=(int32_t)kept; + } + break; + } + case UINT64_C(0x4320e9a2e32eac38): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,8,0); + table_retain_discard(body_keep,1,8); + if(!meta_load_message_body_retain(r,&value->meta, retention))return 0; + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_depot_wire_name__retain( TableWriter * w, const Depot * value , TableRetainWalk retention) +{ + (void)retention; + + if ( value->name_length < 0 || value->name_length > 12 ) { return 0; + } + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int schema_graphdemo_depot_wire_banks__retain( TableWriter * w, const Depot * value , TableRetainWalk retention) +{ + (void)retention; + + uint64_t count = 0; + int32_t i; + for ( i = 0; i < TIER_MAX; i++ ) + { + retention=table_retain_index(retention,(uint32_t)(i)); + retention=table_retain_index(retention,(uint32_t)(i)); + TableWriter slot_probe = table_retain_probe( w , retention); + slot_probe.check_default = 1; + if ( !layer_save_body_retain( &slot_probe, &value->banks[i] , retention) ) { return 0; + } + table_retain_rewind(&slot_probe, retention); + if ( slot_probe.offset == 1 ) { continue; + } + count++; + } + table_writer_put8( w, 13 ); + table_writer_leb( w, count ); + for ( i = 0; i < TIER_MAX; i++ ) + { + retention=table_retain_index(retention,(uint32_t)(i)); + retention=table_retain_index(retention,(uint32_t)(i)); + TableWriter slot_probe = table_retain_probe( w , retention); + slot_probe.check_default = 1; + if ( !layer_save_body_retain( &slot_probe, &value->banks[i] , retention) ) { return 0; + } + table_retain_rewind(&slot_probe, retention); + if ( slot_probe.offset == 1 ) { continue; + } + switch ( i + 1 ) + { + case TIER_NONE: table_writer_leb( w, 0 ); + break; + case TIER_LOW: table_retain_writer_id( w, 0x24f3a319b88552c1ull , retention); + break; + case TIER_HIGH: table_retain_writer_id( w, 0x9deeefd89ca8a81dull , retention); + break; + default: return 0; + /* no declared identity */ + } + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !layer_save_body_retain( w, &value->banks[i] , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !layer_save_body_retain( &probe, &value->banks[i] , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !layer_save_body_retain( w, &value->banks[i] , retention) ) { return 0; + } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int depot_save_body_retain( TableWriter * w, const Depot * value , TableRetainWalk retention) +{ + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* name */ + retention=table_retain_step(body_keep,0,0); + if ( value->name_length < 0 || value->name_length > 12 ) { return 0; + } + if ( value->name_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xc4bcadba8e631b86ull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_depot_wire_name__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_depot_wire_name__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_depot_wire_name__retain( w, value , retention) ) { return 0; + } + } + } + } + { /* banks */ + retention=table_retain_step(body_keep,1,0); + int rides = 0; + int32_t i; + for ( i = 0; i < TIER_MAX; i++ ) + { + retention=table_retain_index(retention,(uint32_t)(i)); + TableWriter slot_probe = table_retain_probe( w , retention); + slot_probe.check_default = 1; + if ( !layer_save_body_retain( &slot_probe, &value->banks[i] , retention) ) { return 0; + } + table_retain_rewind(&slot_probe, retention); + if ( slot_probe.offset == 1 ) { continue; + } + rides = 1; + break; + } + if ( rides ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xdd9eb981e152e90cull , retention); + table_writer_put8( w, 16 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_depot_wire_banks__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_depot_wire_banks__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_depot_wire_banks__retain( w, value , retention) ) { return 0; + } + } + } + } + { /* spare */ + retention=table_retain_step(body_keep,2,0); + if ( value->spare_present ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x4339ee8ab21c8380ull , retention); + table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !meta_save_body_retain( w, &value->spare , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !meta_save_body_retain( &probe, &value->spare , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !meta_save_body_retain( w, &value->spare , retention) ) { return 0; + } + } + } + } + { /* head */ + retention=table_retain_step(body_keep,3,0); + if ( !( value->head.value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x0a8f12cc5f9a0c03ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } + } + } + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int depot_load_body_retain( TableReader * r, Depot * value , TableRetainWalk retention) +{ + (void)retention; + + depot_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + for ( ;; ) + { + uint64_t ref, id; + uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); + kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) + { + case 0xc4bcadba8e631b86ull: /* name */ + { + if ( kind != 12 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; + value->name[0] = 0; + value->name_length = 0; + break; + } + keep = (uint64_t) sub.size; + if ( keep > 12 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 12 ); + r->report->clamped++; + } + memcpy( value->name, sub.buffer, (size_t) keep ); + value->name[keep] = 0; + value->name_length = (int32_t) keep; + break; + } + case 0xdd9eb981e152e90cull: /* banks */ + { + if ( kind != 16 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( sub.size >= 2 ) + { + uint8_t elem_kind; + uint64_t count, i; + if ( !table_reader_array_header( &sub, r, &elem_kind, &count ) ) { r->report->malformed = 1; + break; + } + if ( elem_kind != 13 ) { if ( !(table_kind_widens(elem_kind,13)) ) { r->report->kind_mismatch++; + break; + } r->report->widened++; + } + for ( i = 0; i < count; i++ ) + { + uint64_t key_ref, key; + int32_t slot = -1; + TableReader elem; + if ( !table_reader_leb( &sub, &key_ref ) || key_ref == 0 || key_ref > sub.id_count || !table_reader_span( &sub, &elem ) ) { r->report->malformed = 1; + break; + } + key = table_reader_id_at( &sub, key_ref ); + switch ( key ) + { + case 0x24f3a319b88552c1ull: slot = 0; + break; + case 0x9deeefd89ca8a81dull: slot = 1; + break; + default: r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + break; + } + if ( slot < 0 ) { continue; + } + retention=table_retain_index(retention,(uint32_t)(slot)); + layer_load_body_retain( &elem, &value->banks[slot] , retention); + if(elem.offset!=elem.size) { r->report->malformed=1; + layer_reset(&value->banks[slot]); + } + } + } + break; + } + case 0x4339ee8ab21c8380ull: /* spare */ + { + if ( kind != 13 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,2,0); + table_retain_discard(body_keep,1,2); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + meta_load_body_retain( &sub, &value->spare , retention); + if ( sub.offset != sub.size ) { r->report->malformed = 1; + meta_reset( &value->spare ); + } + value->spare_present = 1; + break; + } + case 0x0a8f12cc5f9a0c03ull: /* head */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,3,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } + break; + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; + } + } +} + +static SCHEMA_UNUSED int depot_load_message_body_retain(TableMessageReader * r,Depot * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + depot_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,12); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0xdd9eb981e152e90c): { + if(!(entry->kind==16 && entry->elem_kind==13)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + {uint64_t n,i; + TableMessageEntry element_shape=table_message_element(entry); + const TableMessageEntry * element_entry=&element_shape; + (void)element_entry; + if(!table_message_count(r,entry,&n))goto malformed; + for(i=0;iid){ + case UINT64_C(0x24f3a319b88552c1):slot=0; + break; + case UINT64_C(0x9deeefd89ca8a81d):slot=1; + break; + default:break; + } if(slot<0){r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + { Layer scratch; + memset(&scratch,0,sizeof(scratch)); + TableRetainWalk named_retention=retention; + retention.retain=NULL; + if(!layer_load_message_body_retain(r,&scratch, retention))return 0; + retention=named_retention; + }continue; + } + retention=table_retain_index(retention,(uint32_t)(slot)); + if(!layer_load_message_body_retain(r,&value->banks[slot], retention))return 0; + } } + break; + } + case UINT64_C(0x4339ee8ab21c8380): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,2,0); + table_retain_discard(body_keep,1,2); + if(!meta_load_message_body_retain(r,&value->spare, retention))return 0; + value->spare_present=1; + break; + } + case UINT64_C(0x0a8f12cc5f9a0c03): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,3,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_album_wire_name__retain( TableWriter * w, const Album * value , TableRetainWalk retention) +{ + (void)retention; + + if ( value->name_length < 0 || value->name_length > 16 ) { return 0; + } + table_writer_raw( w, value->name, value->name_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int album_save_body_retain( TableWriter * w, const Album * value , TableRetainWalk retention) +{ + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* name */ + retention=table_retain_step(body_keep,0,0); + if ( value->name_length < 0 || value->name_length > 16 ) { return 0; + } + if ( value->name_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xc4bcadba8e631b86ull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_album_wire_name__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_album_wire_name__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_album_wire_name__retain( w, value , retention) ) { return 0; + } + } + } + } + { /* tint */ + retention=table_retain_step(body_keep,1,0); + TableWriter default_probe = table_retain_probe( w , retention); + default_probe.check_default = 1; + if ( !colour_save_body_retain( &default_probe, &value->tint , retention) ) { return 0; + } + table_retain_rewind(&default_probe, retention); + if ( default_probe.offset > 1 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x1e4984ef2e958a4cull , retention); + table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !colour_save_body_retain( w, &value->tint , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !colour_save_body_retain( &probe, &value->tint , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !colour_save_body_retain( w, &value->tint , retention) ) { return 0; + } + } + } + } + { /* stamp */ + retention=table_retain_step(body_keep,2,0); + TableWriter default_probe = table_retain_probe( w , retention); + default_probe.check_default = 1; + if ( !stamp_save_body_retain( &default_probe, &value->stamp , retention) ) { return 0; + } + table_retain_rewind(&default_probe, retention); + if ( default_probe.offset > 1 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xee7ba9ad45c64144ull , retention); + table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !stamp_save_body_retain( w, &value->stamp , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !stamp_save_body_retain( &probe, &value->stamp , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !stamp_save_body_retain( w, &value->stamp , retention) ) { return 0; + } + } } } - { - const TreeNode * pointee; - dst->tree.value = 0; /* tree */ - pointee = tree_node_at( ctx, &src->tree ); - if ( pointee != NULL ) + { /* marker */ + retention=table_retain_step(body_keep,3,0); + TableWriter default_probe = table_retain_probe( w , retention); + default_probe.check_default = 1; + if ( !marker_save_body_retain( &default_probe, &value->marker , retention) ) { return 0; + } + table_retain_rewind(&default_probe, retention); + if ( default_probe.offset > 1 ) { - int64_t at = table_align_up64( *used ); - TreeNode * child; - if ( at + (int64_t) sizeof( TreeNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( TreeNode ) ); - child = (TreeNode *) (void *) ( base + at ); - dst->tree.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->tree ); - if ( !tree_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xeddcb72b15486e77ull , retention); + table_writer_put8( w, 13 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !marker_save_body_retain( w, &value->marker , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !marker_save_body_retain( &probe, &value->marker , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !marker_save_body_retain( w, &value->marker , retention) ) { return 0; + } + } } } - { - const Settings * pointee; - dst->settings.value = 0; /* settings */ - pointee = settings_at( ctx, &src->settings ); - if ( pointee != NULL ) + { /* pin */ + retention=table_retain_step(body_keep,4,0); + if ( !( value->pin.value == 0 ) ) { - int64_t at = table_align_up64( *used ); - Settings * child; - if ( at + (int64_t) sizeof( Settings ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( Settings ) ); - child = (Settings *) (void *) ( base + at ); - dst->settings.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->settings ); - if ( !settings_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x77af761956600b54ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->pin); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } } } - { - const ListNode * pointee; - dst->alias.value = 0; /* alias */ - pointee = list_node_at( ctx, &src->alias ); - if ( pointee != NULL ) + { /* head */ + retention=table_retain_step(body_keep,5,0); + if ( !( value->head.value == 0 ) ) { - int64_t at = table_align_up64( *used ); - ListNode * child; - if ( at + (int64_t) sizeof( ListNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( ListNode ) ); - child = (ListNode *) (void *) ( base + at ); - dst->alias.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->alias ); - if ( !list_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x0a8f12cc5f9a0c03ull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->head); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } } } - { /* ground (nested by value) */ - if ( !layer_pack( ctx, &src->ground, &dst->ground, base, capacity, used, depth ) ) { return 0; } - } - { int32_t i; for ( i = 0; i < src->layers_count && i < 4; i++ ) /* layers */ - { - if ( !layer_pack( ctx, &src->layers[i], &dst->layers[i], base, capacity, used, depth ) ) { return 0; } - } } - return 1; + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; } -/* scene_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t scene_load_measure_body( TableReader * r, int32_t depth ) +static SCHEMA_UNUSED int album_load_body_retain( TableReader * r, Album * value , TableRetainWalk retention) { - int64_t bytes = 0; + (void)retention; + + album_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); for ( ;; ) { - uint16_t field_id; + uint64_t ref, id; uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { return bytes; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return bytes; } - if ( !table_reader_has( r, 1 ) ) { return bytes; } + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); - switch ( field_id ) + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) { - case 0x79aa: /* head (*ListNode) */ + case 0xc4bcadba8e631b86ull: /* name */ { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) + if ( kind != 12 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ); - bytes += list_node_load_measure_body( &sub, depth + 1 ); + r->report->malformed = 1; + value->name[0] = 0; + value->name_length = 0; + break; + } + keep = (uint64_t) sub.size; + if ( keep > 16 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 16 ); + r->report->clamped++; } - r->offset += body_len; + memcpy( value->name, sub.buffer, (size_t) keep ); + value->name[keep] = 0; + value->name_length = (int32_t) keep; break; } - case 0x595e: /* tree (*TreeNode) */ + case 0x1e4984ef2e958a4cull: /* tint */ { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) + if ( kind != 13 ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( TreeNode ) ); - bytes += tree_node_load_measure_body( &sub, depth + 1 ); + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + table_retain_discard(body_keep,1,1); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + colour_load_body_retain( &sub, &value->tint , retention); + if ( sub.offset != sub.size ) { r->report->malformed = 1; + colour_reset( &value->tint ); } - r->offset += body_len; break; } - case 0x130e: /* settings (*Settings) */ + case 0xee7ba9ad45c64144ull: /* stamp */ { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) + if ( kind != 13 ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( Settings ) ); - bytes += settings_load_measure_body( &sub, depth + 1 ); + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,2,0); + table_retain_discard(body_keep,1,2); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + stamp_load_body_retain( &sub, &value->stamp , retention); + if ( sub.offset != sub.size ) { r->report->malformed = 1; + stamp_reset( &value->stamp ); } - r->offset += body_len; break; } - case 0xfc71: /* alias (*ListNode) */ + case 0xeddcb72b15486e77ull: /* marker */ { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) + if ( kind != 13 ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ); - bytes += list_node_load_measure_body( &sub, depth + 1 ); + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,3,0); + table_retain_discard(body_keep,1,3); + TableReader sub; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + marker_load_body_retain( &sub, &value->marker , retention); + if ( sub.offset != sub.size ) { r->report->malformed = 1; + marker_reset( &value->marker ); } - r->offset += body_len; break; } - case 0x5bdf: /* ground (Layer nested by value) */ + case 0x77af761956600b54ull: /* pin */ { - uint32_t body_len; - int64_t body_end; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - body_end = r->offset + body_len; + if ( kind != 17 ) { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += layer_load_measure_body( &sub, depth ); + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,4,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; } - r->offset = body_end; + table_node_resolve((*r).nodes,&value->pin,index,UINT64_C(0xd6458a3eef83d457),r->report); + } break; } - case 0x1ee8: /* layers (Layer nested by value) */ + case 0x0a8f12cc5f9a0c03ull: /* head */ { - uint32_t body_len; - int64_t body_end; - if ( kind != 14 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) + if ( kind != 17 ) { - uint32_t count; - TableReader elems; - uint32_t i; - table_reader_get8( r ); /* element kind */ - count = table_reader_get32( r ); - elems = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < count && i < (uint32_t) ( 4 ); i++ ) - { - uint32_t elem_len; - TableReader elem; - if ( !table_reader_has( &elems, 4 ) ) { break; } - elem_len = table_reader_get32( &elems ); - if ( !table_reader_has( &elems, elem_len ) ) { break; } - elem = table_reader_make( elems.buffer + elems.offset, elem_len, r->report ); - bytes += layer_load_measure_body( &elem, depth ); - elems.offset += elem_len; + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; } + break; } - r->offset = body_end; + retention=table_retain_step(body_keep,5,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; + } + table_node_resolve((*r).nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } break; } - default: - { - if ( !table_reader_skip( r, kind ) ) { return bytes; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } break; - } + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; } } } -/* depot_pack_measure: the packed region bytes of everything Depot POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t depot_pack_measure( const TableCtx * ctx, const Depot * value, int32_t depth ) +static SCHEMA_UNUSED int album_load_message_body_retain(TableMessageReader * r,Album * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + album_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0xc4bcadba8e631b86): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,16); + memcpy(value->name,text,(size_t)kept); + value->name[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->name_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x1e4984ef2e958a4c): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + table_retain_discard(body_keep,1,1); + if(!colour_load_message_body_retain(r,&value->tint, retention))return 0; + break; + } + case UINT64_C(0xee7ba9ad45c64144): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,2,0); + table_retain_discard(body_keep,1,2); + if(!stamp_load_message_body_retain(r,&value->stamp, retention))return 0; + break; + } + case UINT64_C(0xeddcb72b15486e77): { + if(!(entry->kind==13 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,13)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,3,0); + table_retain_discard(body_keep,1,3); + if(!marker_load_message_body_retain(r,&value->marker, retention))return 0; + break; + } + case UINT64_C(0x77af761956600b54): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,4,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->pin,index,UINT64_C(0xd6458a3eef83d457),r->report); + } + break; + } + case UINT64_C(0x0a8f12cc5f9a0c03): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,5,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->head,index,UINT64_C(0xf60ec899a5a69fa9),r->report); + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_settings_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) +{ + const Settings * value=(const Settings *)storage; Settings * out=(Settings *)(void *)target; + (void)n; (void)value; (void)out; (void)at; + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_settings_wire_extent_(TableReader r,int64_t * at) { - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - { - const ListNode * pointee = list_node_at( ctx, &value->head ); /* head */ - if ( pointee != NULL ) - { - int64_t inner = list_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ) + inner; + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + default: break; } + if(!table_reader_skip(&r,kind)) { return 1; } } - { int32_t i; for ( i = 0; i < (int32_t) ( TIER_MAX ); i++ ) /* banks */ - { - int64_t inner = layer_pack_measure( ctx, &value->banks[i], depth ); - if ( inner < 0 ) { return -1; } - bytes += inner; - } } - return bytes; } - -/* depot_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. - - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int depot_pack( const TableCtx * ctx, const Depot * src, Depot * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) +static SCHEMA_UNUSED int64_t schema_graphdemo_settings_wire_storage_(const TableReader * r) { - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( Depot ) ); /* a plain struct, by construction */ - { - const ListNode * pointee; - dst->head.value = 0; /* head */ - pointee = list_node_at( ctx, &src->head ); - if ( pointee != NULL ) - { - int64_t at = table_align_up64( *used ); - ListNode * child; - if ( at + (int64_t) sizeof( ListNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( ListNode ) ); - child = (ListNode *) (void *) ( base + at ); - dst->head.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->head ); - if ( !list_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + int64_t at=sizeof(Settings); if(!schema_graphdemo_settings_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) +{ + const ListNode * value=(const ListNode *)storage; ListNode * out=(ListNode *)(void *)target; + (void)n; (void)value; (void)out; (void)at; + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->next); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->next.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->next) : 0; } + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_wire_extent_(TableReader r,int64_t * at) +{ + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + default: break; } + if(!table_reader_skip(&r,kind)) { return 1; } } - { int32_t i; for ( i = 0; i < (int32_t) ( TIER_MAX ); i++ ) /* banks */ - { - if ( !layer_pack( ctx, &src->banks[i], &dst->banks[i], base, capacity, used, depth ) ) { return 0; } - } } +} +static SCHEMA_UNUSED int64_t schema_graphdemo_list_node_wire_storage_(const TableReader * r) +{ + int64_t at=sizeof(ListNode); if(!schema_graphdemo_list_node_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) +{ + const TreeNode * value=(const TreeNode *)storage; TreeNode * out=(TreeNode *)(void *)target; + (void)n; (void)value; (void)out; (void)at; + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->left); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->left.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->left) : 0; } + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->right); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->right.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->right) : 0; } return 1; } - -/* depot_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t depot_load_measure_body( TableReader * r, int32_t depth ) +static SCHEMA_UNUSED int schema_graphdemo_tree_node_wire_extent_(TableReader r,int64_t * at) { - int64_t bytes = 0; - for ( ;; ) - { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { return bytes; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return bytes; } - if ( !table_reader_has( r, 1 ) ) { return bytes; } - kind = table_reader_get8( r ); - switch ( field_id ) - { - case 0x79aa: /* head (*ListNode) */ - { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ); - bytes += list_node_load_measure_body( &sub, depth + 1 ); - } - r->offset += body_len; - break; - } - case 0x7f34: /* banks (Layer nested by value) */ - { - uint32_t body_len; - int64_t body_end; - if ( kind != 16 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - body_end = r->offset + body_len; - if ( body_len >= 5 ) - { - uint32_t count; - TableReader elems; - uint32_t i; - table_reader_get8( r ); /* element kind */ - count = table_reader_get32( r ); - elems = table_reader_make( r->buffer + r->offset, body_end - r->offset, r->report ); - for ( i = 0; i < count && i < (uint32_t) ( TIER_MAX ); i++ ) - { - uint32_t elem_len; - TableReader elem; - if ( !table_reader_has( &elems, 2 ) ) { break; } - table_reader_get16( &elems ); /* the slot's variant id */ - if ( !table_reader_has( &elems, 4 ) ) { break; } - elem_len = table_reader_get32( &elems ); - if ( !table_reader_has( &elems, elem_len ) ) { break; } - elem = table_reader_make( elems.buffer + elems.offset, elem_len, r->report ); - bytes += layer_load_measure_body( &elem, depth ); - elems.offset += elem_len; - } - } - r->offset = body_end; - break; - } - default: - { - if ( !table_reader_skip( r, kind ) ) { return bytes; } - break; - } + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + default: break; } + if(!table_reader_skip(&r,kind)) { return 1; } } } - -/* album_pack_measure: the packed region bytes of everything Album POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t album_pack_measure( const TableCtx * ctx, const Album * value, int32_t depth ) +static SCHEMA_UNUSED int64_t schema_graphdemo_tree_node_wire_storage_(const TableReader * r) { - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - { - const Marker * pointee = marker_at( ctx, &value->pin ); /* pin */ - if ( pointee != NULL ) - { - int64_t inner = marker_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( Marker ) ) + inner; + int64_t at=sizeof(TreeNode); if(!schema_graphdemo_tree_node_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_layer_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) +{ + const Layer * value=(const Layer *)storage; Layer * out=(Layer *)(void *)target; + (void)n; (void)value; (void)out; (void)at; + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->head); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->head.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->head) : 0; } + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_layer_wire_extent_(TableReader r,int64_t * at) +{ + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + default: break; } + if(!table_reader_skip(&r,kind)) { return 1; } } - { - const ListNode * pointee = list_node_at( ctx, &value->head ); /* head */ - if ( pointee != NULL ) - { - int64_t inner = list_node_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ) + inner; +} +static SCHEMA_UNUSED int64_t schema_graphdemo_layer_wire_storage_(const TableReader * r) +{ + int64_t at=sizeof(Layer); if(!schema_graphdemo_layer_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_scene_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) +{ + const Scene * value=(const Scene *)storage; Scene * out=(Scene *)(void *)target; + (void)n; (void)value; (void)out; (void)at; + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->head); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->head.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->head) : 0; } + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->tree); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->tree.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->tree) : 0; } + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->settings); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->settings.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->settings) : 0; } + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->alias); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->alias.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->alias) : 0; } + if(!schema_graphdemo_layer_extent_(n,&value->ground,target ? (uint8_t *)(void *)&out->ground : NULL,at)) { return 0; } + { int32_t extent_i1; for(extent_i1=0;extent_i1layers_count;extent_i1++) { + if(!schema_graphdemo_layer_extent_(n,&value->layers[extent_i1],target ? (uint8_t *)(void *)&out->layers[extent_i1] : NULL,at)) { return 0; } + } } + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_scene_wire_extent_(TableReader r,int64_t * at) +{ + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + case UINT64_C(0x60839e2395be697e): if(kind==13) { + { TableReader extent_body1; + if(!table_reader_span(&r,&extent_body1)) { return 1; } + if(!schema_graphdemo_layer_wire_extent_(extent_body1,at)) { return 0; } + } + continue; } break; + case UINT64_C(0x4554e34a747022df): if(kind==14) { + { TableReader extent_body2; + if(!table_reader_span(&r,&extent_body2)) { return 1; } + if(extent_body2.size>=2) { uint64_t extent_body2_count; + uint8_t wire_kind=table_reader_get8(&extent_body2); + if((wire_kind!=13 && !(table_kind_widens(wire_kind,13))) || !table_reader_leb(&extent_body2,&extent_body2_count)) { goto extent_body2_done; } + { uint64_t i; for(i=0;imarker, depth ); - if ( inner < 0 ) { return -1; } - bytes += inner; - } - return bytes; } - -/* album_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. - - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int album_pack( const TableCtx * ctx, const Album * src, Album * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) +static SCHEMA_UNUSED int64_t schema_graphdemo_scene_wire_storage_(const TableReader * r) { - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( Album ) ); /* a plain struct, by construction */ - { - const Marker * pointee; - dst->pin.value = 0; /* pin */ - pointee = marker_at( ctx, &src->pin ); - if ( pointee != NULL ) - { - int64_t at = table_align_up64( *used ); - Marker * child; - if ( at + (int64_t) sizeof( Marker ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( Marker ) ); - child = (Marker *) (void *) ( base + at ); - dst->pin.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->pin ); - if ( !marker_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } - } + int64_t at=sizeof(Scene); if(!schema_graphdemo_scene_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_depot_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) +{ + const Depot * value=(const Depot *)storage; Depot * out=(Depot *)(void *)target; + (void)n; (void)value; (void)out; (void)at; + { int32_t extent_i1; for(extent_i1=0;extent_i1<2;extent_i1++) { + if(!schema_graphdemo_layer_extent_(n,&value->banks[extent_i1],target ? (uint8_t *)(void *)&out->banks[extent_i1] : NULL,at)) { return 0; } + } } + if(value->spare_present) { } - { - const ListNode * pointee; - dst->head.value = 0; /* head */ - pointee = list_node_at( ctx, &src->head ); - if ( pointee != NULL ) - { - int64_t at = table_align_up64( *used ); - ListNode * child; - if ( at + (int64_t) sizeof( ListNode ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( ListNode ) ); - child = (ListNode *) (void *) ( base + at ); - dst->head.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->head ); - if ( !list_node_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->head); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->head.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->head) : 0; } + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_depot_wire_extent_(TableReader r,int64_t * at) +{ + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + case UINT64_C(0xdd9eb981e152e90c): if(kind==16) { + { TableReader extent_body1; + if(!table_reader_span(&r,&extent_body1)) { return 1; } + if(extent_body1.size>=2) { uint64_t extent_body1_count; + uint8_t wire_kind=table_reader_get8(&extent_body1); + if((wire_kind!=13 && !(table_kind_widens(wire_kind,13))) || !table_reader_leb(&extent_body1,&extent_body1_count)) { goto extent_body1_done; } + { uint64_t i; for(i=0;imarker, &dst->marker, base, capacity, used, depth ) ) { return 0; } +} +static SCHEMA_UNUSED int64_t schema_graphdemo_depot_wire_storage_(const TableReader * r) +{ + int64_t at=sizeof(Depot); if(!schema_graphdemo_depot_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_album_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) +{ + const Album * value=(const Album *)storage; Album * out=(Album *)(void *)target; + (void)n; (void)value; (void)out; (void)at; + if(!schema_graphdemo_marker_extent_(n,&value->marker,target ? (uint8_t *)(void *)&out->marker : NULL,at)) { return 0; } + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->pin); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->pin.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->pin) : 0; } + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->head); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->head.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->head) : 0; } + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_album_wire_extent_(TableReader r,int64_t * at) +{ + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + case UINT64_C(0xeddcb72b15486e77): if(kind==13) { + { TableReader extent_body1; + if(!table_reader_span(&r,&extent_body1)) { return 1; } + if(!schema_graphdemo_marker_wire_extent_(extent_body1,at)) { return 0; } + } + continue; } break; + default: break; + } + if(!table_reader_skip(&r,kind)) { return 1; } } +} +static SCHEMA_UNUSED int64_t schema_graphdemo_album_wire_storage_(const TableReader * r) +{ + int64_t at=sizeof(Album); if(!schema_graphdemo_album_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_settings_node_save_( TableWriter * w, const void * storage ) +{ return settings_save_body(w,(const Settings *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_settings_graph_edges_( TableNumbering * numbering, const void * storage ) +{ + const Settings * value=(const Settings *)storage; + (void)value; (void)numbering; return 1; } +static const TableNodeType schema_graphdemo_settings_node_type_ = { UINT64_C(0x9d8b8aa2b404c2c8), sizeof(Settings), schema_graphdemo_settings_node_save_, schema_graphdemo_settings_graph_edges_, 0, schema_graphdemo_settings_extent_, schema_graphdemo_settings_wire_storage_, 4, 0, schema_graphdemo_settings_cook_body_, NULL }; -/* album_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t album_load_measure_body( TableReader * r, int32_t depth ) +static SCHEMA_UNUSED int schema_graphdemo_list_node_node_save_( TableWriter * w, const void * storage ) +{ return list_node_save_body(w,(const ListNode *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_list_node_graph_edges_( TableNumbering * numbering, const void * storage ) { - int64_t bytes = 0; - for ( ;; ) - { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { return bytes; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return bytes; } - if ( !table_reader_has( r, 1 ) ) { return bytes; } - kind = table_reader_get8( r ); - switch ( field_id ) - { - case 0x69d5: /* pin (*Marker) */ - { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( Marker ) ); - bytes += marker_load_measure_body( &sub, depth + 1 ); - } - r->offset += body_len; - break; - } - case 0x79aa: /* head (*ListNode) */ - { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( ListNode ) ); - bytes += list_node_load_measure_body( &sub, depth + 1 ); - } - r->offset += body_len; - break; - } - case 0x866f: /* marker (Marker nested by value) */ - { - uint32_t body_len; - int64_t body_end; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - body_end = r->offset + body_len; - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += marker_load_measure_body( &sub, depth ); - } - r->offset = body_end; - break; - } - default: - { - if ( !table_reader_skip( r, kind ) ) { return bytes; } - break; - } - } + const ListNode * value=(const ListNode *)storage; + (void)value; (void)numbering; + if (!table_number_slot(numbering,&value->next,&schema_graphdemo_list_node_node_type_)) { return 0; } + return 1; +} +static const TableNodeType schema_graphdemo_list_node_node_type_ = { UINT64_C(0xf60ec899a5a69fa9), sizeof(ListNode), schema_graphdemo_list_node_node_save_, schema_graphdemo_list_node_graph_edges_, 0, schema_graphdemo_list_node_extent_, schema_graphdemo_list_node_wire_storage_, 8, 0, schema_graphdemo_list_node_cook_body_, NULL }; + +static SCHEMA_UNUSED int schema_graphdemo_tree_node_node_save_( TableWriter * w, const void * storage ) +{ return tree_node_save_body(w,(const TreeNode *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_tree_node_graph_edges_( TableNumbering * numbering, const void * storage ) +{ + const TreeNode * value=(const TreeNode *)storage; + (void)value; (void)numbering; + if (!table_number_slot(numbering,&value->left,&schema_graphdemo_tree_node_node_type_)) { return 0; } + if (!table_number_slot(numbering,&value->right,&schema_graphdemo_tree_node_node_type_)) { return 0; } + return 1; +} +static const TableNodeType schema_graphdemo_tree_node_node_type_ = { UINT64_C(0xb97e90a3784c431d), sizeof(TreeNode), schema_graphdemo_tree_node_node_save_, schema_graphdemo_tree_node_graph_edges_, 0, schema_graphdemo_tree_node_extent_, schema_graphdemo_tree_node_wire_storage_, 8, 0, schema_graphdemo_tree_node_cook_body_, NULL }; + +static SCHEMA_UNUSED int schema_graphdemo_layer_node_save_( TableWriter * w, const void * storage ) +{ return layer_save_body(w,(const Layer *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_layer_graph_edges_( TableNumbering * numbering, const void * storage ) +{ + const Layer * value=(const Layer *)storage; + (void)value; (void)numbering; + if (!table_number_slot(numbering,&value->head,&schema_graphdemo_list_node_node_type_)) { return 0; } + return 1; +} +static const TableNodeType schema_graphdemo_layer_node_type_ = { UINT64_C(0x9d167ef77aed79b6), sizeof(Layer), schema_graphdemo_layer_node_save_, schema_graphdemo_layer_graph_edges_, 0, schema_graphdemo_layer_extent_, schema_graphdemo_layer_wire_storage_, 8, 0, schema_graphdemo_layer_cook_body_, NULL }; + +static SCHEMA_UNUSED int schema_graphdemo_scene_node_save_( TableWriter * w, const void * storage ) +{ return scene_save_body(w,(const Scene *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_scene_graph_edges_( TableNumbering * numbering, const void * storage ) +{ + const Scene * value=(const Scene *)storage; + (void)value; (void)numbering; + if (!table_number_slot(numbering,&value->head,&schema_graphdemo_list_node_node_type_)) { return 0; } + if (!table_number_slot(numbering,&value->tree,&schema_graphdemo_tree_node_node_type_)) { return 0; } + if (!table_number_slot(numbering,&value->settings,&schema_graphdemo_settings_node_type_)) { return 0; } + if (!table_number_slot(numbering,&value->alias,&schema_graphdemo_list_node_node_type_)) { return 0; } + if (!schema_graphdemo_layer_graph_edges_(numbering,&value->ground)) { return 0; } + if (value->layers_count<0 || value->layers_count>4) { return 0; } + { int32_t edge_i1; for (edge_i1=0; edge_i1layers_count; edge_i1++) { + if (!schema_graphdemo_layer_graph_edges_(numbering,&value->layers[edge_i1])) { return 0; } + } } + return 1; +} +static const TableNodeType schema_graphdemo_scene_node_type_ = { UINT64_C(0x4a9a31623ab5f213), sizeof(Scene), schema_graphdemo_scene_node_save_, schema_graphdemo_scene_graph_edges_, 0, schema_graphdemo_scene_extent_, schema_graphdemo_scene_wire_storage_, 8, 0, schema_graphdemo_scene_cook_body_, NULL }; + +static SCHEMA_UNUSED int schema_graphdemo_depot_node_save_( TableWriter * w, const void * storage ) +{ return depot_save_body(w,(const Depot *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_depot_graph_edges_( TableNumbering * numbering, const void * storage ) +{ + const Depot * value=(const Depot *)storage; + (void)value; (void)numbering; + { int32_t edge_i1; for (edge_i1=0; edge_i1<2; edge_i1++) { + if (!schema_graphdemo_layer_graph_edges_(numbering,&value->banks[edge_i1])) { return 0; } + } } + if (value->spare_present) { } + if (!table_number_slot(numbering,&value->head,&schema_graphdemo_list_node_node_type_)) { return 0; } + return 1; +} +static const TableNodeType schema_graphdemo_depot_node_type_ = { UINT64_C(0x327fe6dc702553fd), sizeof(Depot), schema_graphdemo_depot_node_save_, schema_graphdemo_depot_graph_edges_, 0, schema_graphdemo_depot_extent_, schema_graphdemo_depot_wire_storage_, 8, 0, schema_graphdemo_depot_cook_body_, NULL }; + +static SCHEMA_UNUSED int schema_graphdemo_album_node_save_( TableWriter * w, const void * storage ) +{ return album_save_body(w,(const Album *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_album_graph_edges_( TableNumbering * numbering, const void * storage ) +{ + const Album * value=(const Album *)storage; + (void)value; (void)numbering; + if (!schema_graphdemo_marker_graph_edges_(numbering,&value->marker)) { return 0; } + if (!table_number_slot(numbering,&value->pin,&schema_graphdemo_marker_node_type_)) { return 0; } + if (!table_number_slot(numbering,&value->head,&schema_graphdemo_list_node_node_type_)) { return 0; } + return 1; } +static const TableNodeType schema_graphdemo_album_node_type_ = { UINT64_C(0xd858c2cb7f1514cc), sizeof(Album), schema_graphdemo_album_node_save_, schema_graphdemo_album_graph_edges_, 0, schema_graphdemo_album_extent_, schema_graphdemo_album_wire_storage_, 8, 0, schema_graphdemo_album_cook_body_, NULL }; /* ---- ListNode: the variable-length life (docs/SPEC-TABLES.md §2, §6, §9) ---- @@ -3919,10 +9993,10 @@ typedef struct ListNodeBuilder /* Allocate a node in THIS thread's front: no lock, no atomic per node. One worker per thread; allocate on your own, and synchronize your own writes to nodes another worker allocated (§6.4). */ -static SCHEMA_UNUSED int list_node_builder_init( ListNodeBuilder * builder ) +static SCHEMA_UNUSED int list_node_builder_init_with_allocator( ListNodeBuilder * builder, TableAllocator allocator ) { uint32_t at; - table_arena_init( &builder->arena ); + table_arena_init_with_allocator( &builder->arena, allocator ); builder->main = table_worker_make( &builder->arena ); builder->root_ref.value = 0; builder->region = NULL; @@ -3937,10 +10011,12 @@ static SCHEMA_UNUSED int list_node_builder_init( ListNodeBuilder * builder ) return 1; } +static SCHEMA_UNUSED int list_node_builder_init(ListNodeBuilder * builder) { return list_node_builder_init_with_allocator(builder,table_default_allocator()); } + static SCHEMA_UNUSED void list_node_builder_shutdown( ListNodeBuilder * builder ) { table_arena_shutdown( &builder->arena ); - free( builder->region ); + table_release(builder->arena.allocator,builder->region); builder->region = NULL; builder->region_bytes = 0; } @@ -3952,119 +10028,375 @@ static SCHEMA_UNUSED ListNode * list_node_builder_root( ListNodeBuilder * builde return (ListNode *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); } -/* Lock is ONE WAY and it is the compaction: the segmented arena becomes one - exact-packed region with zero slack, references rewritten self-relative, - and the mutable life released. Single-threaded: call it after the workers - have joined. The region comes back in builder->region. */ -static SCHEMA_UNUSED int list_node_builder_lock( ListNodeBuilder * builder ) -{ - TableCtx ctx; - const ListNode * root; - int64_t below, total, used; - uint8_t * packed; - if ( builder->arena.locked ) { return builder->region != NULL; } - if ( builder->root_ref.value == 0 ) { return 0; } - ctx.arena = &builder->arena; - root = (const ListNode *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); - below = list_node_pack_measure( &ctx, root, 1 ); - if ( below < 0 ) { return 0; } /* a data cycle, or a chain past kTableMaxDepth */ - total = table_align_up64( (int64_t) sizeof( ListNode ) ) + below; - packed = (uint8_t *) malloc( (size_t) total ); /* the AUTHORING path may allocate */ - if ( packed == NULL ) { return 0; } - memset( packed, 0, (size_t) total ); - used = table_align_up64( (int64_t) sizeof( ListNode ) ); - if ( !list_node_pack( &ctx, root, (ListNode *) (void *) packed, packed, total, &used, 1 ) || used != total ) - { - free( packed ); - return 0; - } - builder->region = packed; - builder->region_bytes = total; - builder->arena.locked = 1; /* MONOTONIC: there is no unlock */ - table_arena_shutdown( &builder->arena ); - return 1; +static SCHEMA_UNUSED int list_node_builder_lock(ListNodeBuilder * builder) +{ + TableCtx ctx; const ListNode * root; uint8_t * packed; int64_t bytes; + if (builder->arena.locked) { return builder->region!=NULL; } + if (builder->root_ref.value==0) { return 0; } + ctx.arena=&builder->arena; root=(const ListNode *)table_arena_at(&builder->arena,(uint32_t)builder->root_ref.value); + packed=table_graph_pack(&ctx,root,&schema_graphdemo_list_node_node_type_,&bytes); + if (packed==NULL) { return 0; } + builder->region=packed; builder->region_bytes=bytes; builder->arena.locked=1; + table_arena_shutdown(&builder->arena); return 1; } -/* ---- ListNode on the wire: the generic, tolerant form (docs/SPEC-TABLES.md §3) ---- - - The CONTEXT says which form the root is in: NULL, or a ctx with a NULL - arena, is a packed REGION — what Lock and Load produce — and a ctx naming - a builder's arena is the mutable form. C++ spells the two as overloads; - one parameter says the same thing here. */ -static SCHEMA_UNUSED int64_t list_node_measure( const TableCtx * ctx, const ListNode * root ) +static SCHEMA_UNUSED const TableNodeType * schema_graphdemo_list_node_node_lookup_(uint64_t id) { - return root != NULL ? list_node_measure_body( ctx, root, 1 ) : -1; + switch (id) { + case UINT64_C(0xf60ec899a5a69fa9): return &schema_graphdemo_list_node_node_type_; + default: return NULL; + } } - -static SCHEMA_UNUSED int64_t list_node_save( const TableCtx * ctx, const ListNode * root, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int64_t list_node_measure_with_allocator(const TableCtx * ctx, const ListNode * root, TableAllocator allocator) { - TableWriter w; - if ( root == NULL ) { return -1; } - w = table_writer_make( buffer, capacity ); - if ( !list_node_save_body( ctx, &w, root, 1 ) ) { return -1; } - return w.offset; /* == list_node_measure( ctx, root ) */ + return table_graph_save(ctx,root,&schema_graphdemo_list_node_node_type_,NULL,INT64_MAX,allocator); } - -/* list_node_load_measure: the exact region bytes a wire buffer will need. The - caller owns the allocation — generated load code allocates nothing. */ -static SCHEMA_UNUSED int64_t list_node_load_measure( const uint8_t * wire, int64_t wire_bytes ) +static SCHEMA_UNUSED int64_t list_node_measure(const TableCtx * ctx,const ListNode * root) +{ return list_node_measure_with_allocator(ctx,root,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t list_node_save_with_allocator(const TableCtx * ctx, const ListNode * root, uint8_t * buffer, int64_t capacity, TableAllocator allocator) { - TableReport ignored; - TableReader r; - int64_t below; - memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( wire, wire_bytes, &ignored ); - below = list_node_load_measure_body( &r, 1 ); - if ( below < 0 ) { below = 0; } - return table_align_up64( (int64_t) sizeof( ListNode ) ) + below; -} - -/* list_node_load: decode the tolerant wire into the caller's exact-sized region and - return the root. Partial results are kept, as everywhere on this wire — - the report says what happened. NULL means the CALLER's buffer was wrong. */ -static SCHEMA_UNUSED const ListNode * list_node_load( uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - TableRegionSink region_sink; - TableSink sink; - ListNode * root; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - if ( region == NULL || region_bytes < (int64_t) sizeof( ListNode ) ) { out->malformed = 1; return NULL; } - if ( ( ( (uintptr_t) region ) & ( kTableAlign - 1 ) ) != 0 ) { out->malformed = 1; return NULL; } - memset( region, 0, (size_t) region_bytes ); - region_sink.base = region; - region_sink.capacity = region_bytes; - region_sink.used = table_align_up64( (int64_t) sizeof( ListNode ) ); - sink.region = ®ion_sink; - sink.worker = NULL; - root = (ListNode *) (void *) region; - r = table_reader_make( wire, wire_bytes, out ); - list_node_load_body( &r, &sink, root, 1 ); - return root; -} - -/* list_node_load_builder: the TOOL's path — the same tolerant decode into a fresh - builder, so loaded data can be edited and locked again. */ -static SCHEMA_UNUSED int list_node_load_builder( ListNodeBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - ListNode * root; - TableSink sink; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - root = list_node_builder_root( builder ); - if ( root == NULL ) { out->malformed = 1; return 0; } - sink.region = NULL; - sink.worker = &builder->main; - r = table_reader_make( wire, wire_bytes, out ); - return list_node_load_body( &r, &sink, root, 1 ); + if (buffer==NULL || capacity<0) { return -1; } + return table_graph_save(ctx,root,&schema_graphdemo_list_node_node_type_,buffer,capacity,allocator); +} +static SCHEMA_UNUSED int64_t list_node_save(const TableCtx * ctx,const ListNode * root, uint8_t * buffer, int64_t capacity) +{ return list_node_save_with_allocator(ctx,root,buffer,capacity,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t schema_graphdemo_list_node_load_layout_(const TableReader * reader, int64_t * data_out, int64_t * records_out) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id; + int64_t data=schema_graphdemo_list_node_wire_storage_(reader), records=0; + if(data<0) { return -1; } + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_list_node_node_lookup_(id); + if (type!=NULL) { int64_t size=table_node_wire_storage(type,&body); if (size<0 || size>INT64_MAX-data) { return -1; } data+=size; } + records++; + } + if (records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1 || (records+1)*(int64_t)sizeof(TableNodeDirEntry)>INT64_MAX-data) { return -1; } + *data_out=data; *records_out=records; return data+(records+1)*(int64_t)sizeof(TableNodeDirEntry); +} +static SCHEMA_UNUSED int64_t list_node_load_measure_ex(const uint8_t * wire, int64_t bytes, int64_t * attribution, TableRefuseReason * reason) +{ + TableReport report; TableReader reader; int64_t data,records,total; memset(&report,0,sizeof(report)); + if (attribution!=NULL) { *attribution=0; } + if (!table_wire_open(&reader,wire,bytes,&report)) { if(report.refused && reason!=NULL) { *reason=SCHEMA_TABLE_REFUSE_UNKNOWN_FORM; } return -1; } + report.reason=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH; total=schema_graphdemo_list_node_load_layout_(&reader,&data,&records); + if(total<0 && reason!=NULL) { *reason=report.reason; } + if(total>=0 && attribution!=NULL) { *attribution=(records+1)*(int64_t)sizeof(TableNodeDirEntry); } return total; +} +static SCHEMA_UNUSED int64_t list_node_load_measure(const uint8_t * wire, int64_t bytes) +{ return list_node_load_measure_ex(wire,bytes,NULL,NULL); } +static SCHEMA_UNUSED int schema_graphdemo_list_node_load_graph_(TableReader * reader, ListNode * root, TableNodeMap * nodes, TableNodeDirEntry * directory, TableSink * sink) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id,k=0; int32_t unknown=0; + list_node_reset(root); + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_list_node_node_lookup_(id); + directory[k+1].type_id=id; directory[k+1].offset=UINT64_MAX; + if (type!=NULL && type->blob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; k++; continue; } + if (type==NULL) { unknown++; } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; return 0; } + directory[k+1].offset=(uint64_t)at; sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; return 0; } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; } else { reader->report->malformed=1; } + if(nodes->good) { + scan=table_node_scan_begin(reader); k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; TableSink carve; const TableNodeType * type=schema_graphdemo_list_node_node_lookup_(id); + carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; extent.used=type->size; extent.capacity=table_node_wire_storage(type,&body); carve.region=&extent; } + nodes->sink=&carve; body.nodes=nodes; + switch(id) { + case UINT64_C(0xf60ec899a5a69fa9): list_node_load_body(&body,(ListNode *)value); break; + default: break; + } + nodes->sink=NULL; if(nodes->refused) { return 0; } + } + k++; + } + } + reader->nodes=nodes; reader->nested=0; + { int read_ok; TableRegionSink extent; TableSink carve; carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; extent.used=sizeof(*root); extent.capacity=schema_graphdemo_list_node_wire_storage_(reader); carve.region=&extent; } + nodes->sink=&carve; read_ok=list_node_load_body(reader,root); nodes->sink=NULL; return !nodes->refused && (!nodes->arena || read_ok); } +} +static SCHEMA_UNUSED const ListNode * list_node_load(uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableRegionSink place; TableSink sink; int64_t data,records,total; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return NULL; } + total=schema_graphdemo_list_node_load_layout_(&reader,&data,&records); + if(total<0 || region==NULL || region_bytesmalformed=1; return NULL; } + memset(region,0,(size_t)total); directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; directory[0].type_id=UINT64_C(0xf60ec899a5a69fa9); + nodes.base=region; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=0; nodes.refused=0; nodes.sink=NULL; + place.base=region; place.capacity=data; place.used=schema_graphdemo_list_node_wire_storage_(&reader); sink.region=&place; sink.worker=NULL; + if(!schema_graphdemo_list_node_load_graph_(&reader,(ListNode *)(void *)region,&nodes,directory,&sink)) { return NULL; } + return (const ListNode *)(const void *)region; +} +static SCHEMA_UNUSED int list_node_load_builder(ListNodeBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableSink sink; TableNodeScan scan; TableReader body; uint64_t id; int64_t records=0; ListNode * root; int ok; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return 0; } + scan=table_node_scan_begin(&reader); while(table_node_scan_next(&scan,&id,&body)) { records++; } + if(records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1) { report->malformed=1; return 0; } + root=list_node_builder_root(builder); if(root==NULL) { report->malformed=1; return 0; } + directory=(TableNodeDirEntry *)table_allocate(builder->arena.allocator,(records+1)*(int64_t)sizeof(TableNodeDirEntry)); + if(directory==NULL) { report->malformed=1; return 0; } + directory[0].offset=(uint64_t)builder->root_ref.value; directory[0].type_id=UINT64_C(0xf60ec899a5a69fa9); + nodes.base=NULL; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=1; nodes.refused=0; nodes.sink=NULL; sink.region=NULL; sink.worker=&builder->main; + ok=schema_graphdemo_list_node_load_graph_(&reader,root,&nodes,directory,&sink); table_release(builder->arena.allocator,directory); return ok; +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_message_graph_save_(TableBitWriter * w,const ListNode * root,TableAllocator allocator) +{ + TableNumbering numbering; + uint64_t i; + int ok=0; + if(!table_number_build(&numbering,NULL,root,&schema_graphdemo_list_node_node_type_,allocator))goto done; + w->nodes=&numbering; + w->index_bits=table_bits_required(0,(int64_t)numbering.count); + if(numbering.count-1>UINT32_MAX)goto done; + if(numbering.count>1){ + table_bit_put(w,37,kTableMessageRefBitsHere); + table_bit_put(w,numbering.count-1,32); + for(i=1;itype->id){ + case UINT64_C(0xf60ec899a5a69fa9): + table_bit_put(w,44,kTableMessageRefBitsHere); + if(!list_node_save_message_body(w,(const ListNode *)node->value))goto done; + break; + default:goto done; + } } }ok=list_node_save_message_body(w,root); + done:table_number_dispose(&numbering); + w->nodes=NULL; + return ok&&!w->overflow; +} +static SCHEMA_UNUSED int64_t list_node_measure_messages_with_allocator(const ListNode * const * roots,int64_t count,TableReport * report,TableAllocator allocator) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(roots==NULL||count<1)return -1; + if(count>kTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL||capacity<0)return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid; + if(*id==UINT64_C(0x2f2ec0474f1c4fe4)||*id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_message_blob_scan(r,length))return 0; + return *size>=0; + }switch(*id){ + case UINT64_C(0xf60ec899a5a69fa9):at=sizeof(ListNode); + if(!schema_graphdemo_list_node_message_extent_(r,&at))return 0; + break; + default:return table_message_skip_body(r); + }if(at>INT64_MAX-kTableAlign)return 0; + *size=table_align_up64(at); + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_message_storage_(TableMessageReader * r,uint64_t * records,int64_t * data,int64_t * root_bytes,int * complete) +{ + uint64_t i,id,length; + int64_t size,root=sizeof(ListNode); + *data=0; + *complete=1; + if(!table_message_nodes_open(r,records))return 0; + for(i=0;i<*records;i++){ + if(!schema_graphdemo_list_node_message_record_scan_(r,&id,&size,&length)||size>INT64_MAX-*data)return 0; + *data+=size; + } + if(!schema_graphdemo_list_node_message_extent_(r,&root))*complete=0; + if(r->extent_refused)return 0; + if(root>INT64_MAX-kTableAlign)return 0; + *root_bytes=table_align_up64(root); + if(*root_bytes>INT64_MAX-*data)return 0; + *data+=*root_bytes; + return 1; +} +static SCHEMA_UNUSED int64_t list_node_load_measure_messages_ex(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,int64_t * attribution,TableRefuseReason * reason) +{ + TableReport report={0}; + TableMessageReader r; + int64_t total=0,dirs=0,bodies,i; + if(attribution)*attribution=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,&report); + if(bodies<0)return -1; + for(i=0;i=(uint64_t)(INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)))return -1; + dir=(int64_t)(records+1)*(int64_t)sizeof(TableNodeDirEntry); + if(data>INT64_MAX-dir || data+dir>INT64_MAX-total)return -1; + total+=data+dir; + dirs+=dir; + if(!complete)break; + }if(attribution)*attribution=dirs; + return total; +} +static SCHEMA_UNUSED int64_t list_node_load_measure_messages(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes) +{ return list_node_load_measure_messages_ex(vocabulary,buffer,bytes,NULL,NULL); +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_message_load_into_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const ListNode ** out) +{ + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(ListNode); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + ListNode * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(ListNode *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0xf60ec899a5a69fa9); + *used+=root_size; + list_node_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body(r,(ListNode *)(void *)value))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=list_node_load_message_body(r,root); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int list_node_load_messages(const ListNode ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t bodies,capacity,used=0,i; + if(report==NULL)report=&ignored; + if(roots==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(region==NULL||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iarena ); + table_arena_init_with_allocator( &builder->arena, allocator ); builder->main = table_worker_make( &builder->arena ); builder->root_ref.value = 0; builder->region = NULL; @@ -4109,10 +10441,12 @@ static SCHEMA_UNUSED int tree_node_builder_init( TreeNodeBuilder * builder ) return 1; } +static SCHEMA_UNUSED int tree_node_builder_init(TreeNodeBuilder * builder) { return tree_node_builder_init_with_allocator(builder,table_default_allocator()); } + static SCHEMA_UNUSED void tree_node_builder_shutdown( TreeNodeBuilder * builder ) { table_arena_shutdown( &builder->arena ); - free( builder->region ); + table_release(builder->arena.allocator,builder->region); builder->region = NULL; builder->region_bytes = 0; } @@ -4124,119 +10458,375 @@ static SCHEMA_UNUSED TreeNode * tree_node_builder_root( TreeNodeBuilder * builde return (TreeNode *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); } -/* Lock is ONE WAY and it is the compaction: the segmented arena becomes one - exact-packed region with zero slack, references rewritten self-relative, - and the mutable life released. Single-threaded: call it after the workers - have joined. The region comes back in builder->region. */ -static SCHEMA_UNUSED int tree_node_builder_lock( TreeNodeBuilder * builder ) -{ - TableCtx ctx; - const TreeNode * root; - int64_t below, total, used; - uint8_t * packed; - if ( builder->arena.locked ) { return builder->region != NULL; } - if ( builder->root_ref.value == 0 ) { return 0; } - ctx.arena = &builder->arena; - root = (const TreeNode *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); - below = tree_node_pack_measure( &ctx, root, 1 ); - if ( below < 0 ) { return 0; } /* a data cycle, or a chain past kTableMaxDepth */ - total = table_align_up64( (int64_t) sizeof( TreeNode ) ) + below; - packed = (uint8_t *) malloc( (size_t) total ); /* the AUTHORING path may allocate */ - if ( packed == NULL ) { return 0; } - memset( packed, 0, (size_t) total ); - used = table_align_up64( (int64_t) sizeof( TreeNode ) ); - if ( !tree_node_pack( &ctx, root, (TreeNode *) (void *) packed, packed, total, &used, 1 ) || used != total ) - { - free( packed ); - return 0; - } - builder->region = packed; - builder->region_bytes = total; - builder->arena.locked = 1; /* MONOTONIC: there is no unlock */ - table_arena_shutdown( &builder->arena ); - return 1; +static SCHEMA_UNUSED int tree_node_builder_lock(TreeNodeBuilder * builder) +{ + TableCtx ctx; const TreeNode * root; uint8_t * packed; int64_t bytes; + if (builder->arena.locked) { return builder->region!=NULL; } + if (builder->root_ref.value==0) { return 0; } + ctx.arena=&builder->arena; root=(const TreeNode *)table_arena_at(&builder->arena,(uint32_t)builder->root_ref.value); + packed=table_graph_pack(&ctx,root,&schema_graphdemo_tree_node_node_type_,&bytes); + if (packed==NULL) { return 0; } + builder->region=packed; builder->region_bytes=bytes; builder->arena.locked=1; + table_arena_shutdown(&builder->arena); return 1; } -/* ---- TreeNode on the wire: the generic, tolerant form (docs/SPEC-TABLES.md §3) ---- - - The CONTEXT says which form the root is in: NULL, or a ctx with a NULL - arena, is a packed REGION — what Lock and Load produce — and a ctx naming - a builder's arena is the mutable form. C++ spells the two as overloads; - one parameter says the same thing here. */ -static SCHEMA_UNUSED int64_t tree_node_measure( const TableCtx * ctx, const TreeNode * root ) +static SCHEMA_UNUSED const TableNodeType * schema_graphdemo_tree_node_node_lookup_(uint64_t id) { - return root != NULL ? tree_node_measure_body( ctx, root, 1 ) : -1; + switch (id) { + case UINT64_C(0xb97e90a3784c431d): return &schema_graphdemo_tree_node_node_type_; + default: return NULL; + } } - -static SCHEMA_UNUSED int64_t tree_node_save( const TableCtx * ctx, const TreeNode * root, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int64_t tree_node_measure_with_allocator(const TableCtx * ctx, const TreeNode * root, TableAllocator allocator) { - TableWriter w; - if ( root == NULL ) { return -1; } - w = table_writer_make( buffer, capacity ); - if ( !tree_node_save_body( ctx, &w, root, 1 ) ) { return -1; } - return w.offset; /* == tree_node_measure( ctx, root ) */ + return table_graph_save(ctx,root,&schema_graphdemo_tree_node_node_type_,NULL,INT64_MAX,allocator); } - -/* tree_node_load_measure: the exact region bytes a wire buffer will need. The - caller owns the allocation — generated load code allocates nothing. */ -static SCHEMA_UNUSED int64_t tree_node_load_measure( const uint8_t * wire, int64_t wire_bytes ) +static SCHEMA_UNUSED int64_t tree_node_measure(const TableCtx * ctx,const TreeNode * root) +{ return tree_node_measure_with_allocator(ctx,root,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t tree_node_save_with_allocator(const TableCtx * ctx, const TreeNode * root, uint8_t * buffer, int64_t capacity, TableAllocator allocator) +{ + if (buffer==NULL || capacity<0) { return -1; } + return table_graph_save(ctx,root,&schema_graphdemo_tree_node_node_type_,buffer,capacity,allocator); +} +static SCHEMA_UNUSED int64_t tree_node_save(const TableCtx * ctx,const TreeNode * root, uint8_t * buffer, int64_t capacity) +{ return tree_node_save_with_allocator(ctx,root,buffer,capacity,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t schema_graphdemo_tree_node_load_layout_(const TableReader * reader, int64_t * data_out, int64_t * records_out) { - TableReport ignored; - TableReader r; - int64_t below; - memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( wire, wire_bytes, &ignored ); - below = tree_node_load_measure_body( &r, 1 ); - if ( below < 0 ) { below = 0; } - return table_align_up64( (int64_t) sizeof( TreeNode ) ) + below; -} - -/* tree_node_load: decode the tolerant wire into the caller's exact-sized region and - return the root. Partial results are kept, as everywhere on this wire — - the report says what happened. NULL means the CALLER's buffer was wrong. */ -static SCHEMA_UNUSED const TreeNode * tree_node_load( uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - TableRegionSink region_sink; - TableSink sink; - TreeNode * root; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - if ( region == NULL || region_bytes < (int64_t) sizeof( TreeNode ) ) { out->malformed = 1; return NULL; } - if ( ( ( (uintptr_t) region ) & ( kTableAlign - 1 ) ) != 0 ) { out->malformed = 1; return NULL; } - memset( region, 0, (size_t) region_bytes ); - region_sink.base = region; - region_sink.capacity = region_bytes; - region_sink.used = table_align_up64( (int64_t) sizeof( TreeNode ) ); - sink.region = ®ion_sink; - sink.worker = NULL; - root = (TreeNode *) (void *) region; - r = table_reader_make( wire, wire_bytes, out ); - tree_node_load_body( &r, &sink, root, 1 ); - return root; -} - -/* tree_node_load_builder: the TOOL's path — the same tolerant decode into a fresh - builder, so loaded data can be edited and locked again. */ -static SCHEMA_UNUSED int tree_node_load_builder( TreeNodeBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - TreeNode * root; - TableSink sink; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - root = tree_node_builder_root( builder ); - if ( root == NULL ) { out->malformed = 1; return 0; } - sink.region = NULL; - sink.worker = &builder->main; - r = table_reader_make( wire, wire_bytes, out ); - return tree_node_load_body( &r, &sink, root, 1 ); + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id; + int64_t data=schema_graphdemo_tree_node_wire_storage_(reader), records=0; + if(data<0) { return -1; } + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_tree_node_node_lookup_(id); + if (type!=NULL) { int64_t size=table_node_wire_storage(type,&body); if (size<0 || size>INT64_MAX-data) { return -1; } data+=size; } + records++; + } + if (records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1 || (records+1)*(int64_t)sizeof(TableNodeDirEntry)>INT64_MAX-data) { return -1; } + *data_out=data; *records_out=records; return data+(records+1)*(int64_t)sizeof(TableNodeDirEntry); +} +static SCHEMA_UNUSED int64_t tree_node_load_measure_ex(const uint8_t * wire, int64_t bytes, int64_t * attribution, TableRefuseReason * reason) +{ + TableReport report; TableReader reader; int64_t data,records,total; memset(&report,0,sizeof(report)); + if (attribution!=NULL) { *attribution=0; } + if (!table_wire_open(&reader,wire,bytes,&report)) { if(report.refused && reason!=NULL) { *reason=SCHEMA_TABLE_REFUSE_UNKNOWN_FORM; } return -1; } + report.reason=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH; total=schema_graphdemo_tree_node_load_layout_(&reader,&data,&records); + if(total<0 && reason!=NULL) { *reason=report.reason; } + if(total>=0 && attribution!=NULL) { *attribution=(records+1)*(int64_t)sizeof(TableNodeDirEntry); } return total; +} +static SCHEMA_UNUSED int64_t tree_node_load_measure(const uint8_t * wire, int64_t bytes) +{ return tree_node_load_measure_ex(wire,bytes,NULL,NULL); } +static SCHEMA_UNUSED int schema_graphdemo_tree_node_load_graph_(TableReader * reader, TreeNode * root, TableNodeMap * nodes, TableNodeDirEntry * directory, TableSink * sink) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id,k=0; int32_t unknown=0; + tree_node_reset(root); + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_tree_node_node_lookup_(id); + directory[k+1].type_id=id; directory[k+1].offset=UINT64_MAX; + if (type!=NULL && type->blob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; k++; continue; } + if (type==NULL) { unknown++; } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; return 0; } + directory[k+1].offset=(uint64_t)at; sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; return 0; } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; } else { reader->report->malformed=1; } + if(nodes->good) { + scan=table_node_scan_begin(reader); k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; TableSink carve; const TableNodeType * type=schema_graphdemo_tree_node_node_lookup_(id); + carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; extent.used=type->size; extent.capacity=table_node_wire_storage(type,&body); carve.region=&extent; } + nodes->sink=&carve; body.nodes=nodes; + switch(id) { + case UINT64_C(0xb97e90a3784c431d): tree_node_load_body(&body,(TreeNode *)value); break; + default: break; + } + nodes->sink=NULL; if(nodes->refused) { return 0; } + } + k++; + } + } + reader->nodes=nodes; reader->nested=0; + { int read_ok; TableRegionSink extent; TableSink carve; carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; extent.used=sizeof(*root); extent.capacity=schema_graphdemo_tree_node_wire_storage_(reader); carve.region=&extent; } + nodes->sink=&carve; read_ok=tree_node_load_body(reader,root); nodes->sink=NULL; return !nodes->refused && (!nodes->arena || read_ok); } +} +static SCHEMA_UNUSED const TreeNode * tree_node_load(uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableRegionSink place; TableSink sink; int64_t data,records,total; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return NULL; } + total=schema_graphdemo_tree_node_load_layout_(&reader,&data,&records); + if(total<0 || region==NULL || region_bytesmalformed=1; return NULL; } + memset(region,0,(size_t)total); directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; directory[0].type_id=UINT64_C(0xb97e90a3784c431d); + nodes.base=region; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=0; nodes.refused=0; nodes.sink=NULL; + place.base=region; place.capacity=data; place.used=schema_graphdemo_tree_node_wire_storage_(&reader); sink.region=&place; sink.worker=NULL; + if(!schema_graphdemo_tree_node_load_graph_(&reader,(TreeNode *)(void *)region,&nodes,directory,&sink)) { return NULL; } + return (const TreeNode *)(const void *)region; +} +static SCHEMA_UNUSED int tree_node_load_builder(TreeNodeBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableSink sink; TableNodeScan scan; TableReader body; uint64_t id; int64_t records=0; TreeNode * root; int ok; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return 0; } + scan=table_node_scan_begin(&reader); while(table_node_scan_next(&scan,&id,&body)) { records++; } + if(records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1) { report->malformed=1; return 0; } + root=tree_node_builder_root(builder); if(root==NULL) { report->malformed=1; return 0; } + directory=(TableNodeDirEntry *)table_allocate(builder->arena.allocator,(records+1)*(int64_t)sizeof(TableNodeDirEntry)); + if(directory==NULL) { report->malformed=1; return 0; } + directory[0].offset=(uint64_t)builder->root_ref.value; directory[0].type_id=UINT64_C(0xb97e90a3784c431d); + nodes.base=NULL; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=1; nodes.refused=0; nodes.sink=NULL; sink.region=NULL; sink.worker=&builder->main; + ok=schema_graphdemo_tree_node_load_graph_(&reader,root,&nodes,directory,&sink); table_release(builder->arena.allocator,directory); return ok; +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_message_graph_save_(TableBitWriter * w,const TreeNode * root,TableAllocator allocator) +{ + TableNumbering numbering; + uint64_t i; + int ok=0; + if(!table_number_build(&numbering,NULL,root,&schema_graphdemo_tree_node_node_type_,allocator))goto done; + w->nodes=&numbering; + w->index_bits=table_bits_required(0,(int64_t)numbering.count); + if(numbering.count-1>UINT32_MAX)goto done; + if(numbering.count>1){ + table_bit_put(w,37,kTableMessageRefBitsHere); + table_bit_put(w,numbering.count-1,32); + for(i=1;itype->id){ + case UINT64_C(0xb97e90a3784c431d): + table_bit_put(w,51,kTableMessageRefBitsHere); + if(!tree_node_save_message_body(w,(const TreeNode *)node->value))goto done; + break; + default:goto done; + } } }ok=tree_node_save_message_body(w,root); + done:table_number_dispose(&numbering); + w->nodes=NULL; + return ok&&!w->overflow; +} +static SCHEMA_UNUSED int64_t tree_node_measure_messages_with_allocator(const TreeNode * const * roots,int64_t count,TableReport * report,TableAllocator allocator) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(roots==NULL||count<1)return -1; + if(count>kTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL||capacity<0)return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid; + if(*id==UINT64_C(0x2f2ec0474f1c4fe4)||*id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_message_blob_scan(r,length))return 0; + return *size>=0; + }switch(*id){ + case UINT64_C(0xb97e90a3784c431d):at=sizeof(TreeNode); + if(!schema_graphdemo_tree_node_message_extent_(r,&at))return 0; + break; + default:return table_message_skip_body(r); + }if(at>INT64_MAX-kTableAlign)return 0; + *size=table_align_up64(at); + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_message_storage_(TableMessageReader * r,uint64_t * records,int64_t * data,int64_t * root_bytes,int * complete) +{ + uint64_t i,id,length; + int64_t size,root=sizeof(TreeNode); + *data=0; + *complete=1; + if(!table_message_nodes_open(r,records))return 0; + for(i=0;i<*records;i++){ + if(!schema_graphdemo_tree_node_message_record_scan_(r,&id,&size,&length)||size>INT64_MAX-*data)return 0; + *data+=size; + } + if(!schema_graphdemo_tree_node_message_extent_(r,&root))*complete=0; + if(r->extent_refused)return 0; + if(root>INT64_MAX-kTableAlign)return 0; + *root_bytes=table_align_up64(root); + if(*root_bytes>INT64_MAX-*data)return 0; + *data+=*root_bytes; + return 1; +} +static SCHEMA_UNUSED int64_t tree_node_load_measure_messages_ex(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,int64_t * attribution,TableRefuseReason * reason) +{ + TableReport report={0}; + TableMessageReader r; + int64_t total=0,dirs=0,bodies,i; + if(attribution)*attribution=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,&report); + if(bodies<0)return -1; + for(i=0;i=(uint64_t)(INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)))return -1; + dir=(int64_t)(records+1)*(int64_t)sizeof(TableNodeDirEntry); + if(data>INT64_MAX-dir || data+dir>INT64_MAX-total)return -1; + total+=data+dir; + dirs+=dir; + if(!complete)break; + }if(attribution)*attribution=dirs; + return total; +} +static SCHEMA_UNUSED int64_t tree_node_load_measure_messages(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes) +{ return tree_node_load_measure_messages_ex(vocabulary,buffer,bytes,NULL,NULL); +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_message_load_into_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const TreeNode ** out) +{ + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(TreeNode); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + TreeNode * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(TreeNode *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0xb97e90a3784c431d); + *used+=root_size; + tree_node_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xb97e90a3784c431d):carve.used=sizeof(TreeNode); + carve.capacity=sizeof(TreeNode); + scan=*r; + if(!schema_graphdemo_tree_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!tree_node_load_message_body(r,(TreeNode *)(void *)value))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=tree_node_load_message_body(r,root); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int tree_node_load_messages(const TreeNode ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t bodies,capacity,used=0,i; + if(report==NULL)report=&ignored; + if(roots==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(region==NULL||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iarena ); + table_arena_init_with_allocator( &builder->arena, allocator ); builder->main = table_worker_make( &builder->arena ); builder->root_ref.value = 0; builder->region = NULL; @@ -4281,10 +10871,12 @@ static SCHEMA_UNUSED int layer_builder_init( LayerBuilder * builder ) return 1; } +static SCHEMA_UNUSED int layer_builder_init(LayerBuilder * builder) { return layer_builder_init_with_allocator(builder,table_default_allocator()); } + static SCHEMA_UNUSED void layer_builder_shutdown( LayerBuilder * builder ) { table_arena_shutdown( &builder->arena ); - free( builder->region ); + table_release(builder->arena.allocator,builder->region); builder->region = NULL; builder->region_bytes = 0; } @@ -4296,119 +10888,375 @@ static SCHEMA_UNUSED Layer * layer_builder_root( LayerBuilder * builder ) return (Layer *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); } -/* Lock is ONE WAY and it is the compaction: the segmented arena becomes one - exact-packed region with zero slack, references rewritten self-relative, - and the mutable life released. Single-threaded: call it after the workers - have joined. The region comes back in builder->region. */ -static SCHEMA_UNUSED int layer_builder_lock( LayerBuilder * builder ) -{ - TableCtx ctx; - const Layer * root; - int64_t below, total, used; - uint8_t * packed; - if ( builder->arena.locked ) { return builder->region != NULL; } - if ( builder->root_ref.value == 0 ) { return 0; } - ctx.arena = &builder->arena; - root = (const Layer *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); - below = layer_pack_measure( &ctx, root, 1 ); - if ( below < 0 ) { return 0; } /* a data cycle, or a chain past kTableMaxDepth */ - total = table_align_up64( (int64_t) sizeof( Layer ) ) + below; - packed = (uint8_t *) malloc( (size_t) total ); /* the AUTHORING path may allocate */ - if ( packed == NULL ) { return 0; } - memset( packed, 0, (size_t) total ); - used = table_align_up64( (int64_t) sizeof( Layer ) ); - if ( !layer_pack( &ctx, root, (Layer *) (void *) packed, packed, total, &used, 1 ) || used != total ) - { - free( packed ); - return 0; - } - builder->region = packed; - builder->region_bytes = total; - builder->arena.locked = 1; /* MONOTONIC: there is no unlock */ - table_arena_shutdown( &builder->arena ); - return 1; +static SCHEMA_UNUSED int layer_builder_lock(LayerBuilder * builder) +{ + TableCtx ctx; const Layer * root; uint8_t * packed; int64_t bytes; + if (builder->arena.locked) { return builder->region!=NULL; } + if (builder->root_ref.value==0) { return 0; } + ctx.arena=&builder->arena; root=(const Layer *)table_arena_at(&builder->arena,(uint32_t)builder->root_ref.value); + packed=table_graph_pack(&ctx,root,&schema_graphdemo_layer_node_type_,&bytes); + if (packed==NULL) { return 0; } + builder->region=packed; builder->region_bytes=bytes; builder->arena.locked=1; + table_arena_shutdown(&builder->arena); return 1; } -/* ---- Layer on the wire: the generic, tolerant form (docs/SPEC-TABLES.md §3) ---- - - The CONTEXT says which form the root is in: NULL, or a ctx with a NULL - arena, is a packed REGION — what Lock and Load produce — and a ctx naming - a builder's arena is the mutable form. C++ spells the two as overloads; - one parameter says the same thing here. */ -static SCHEMA_UNUSED int64_t layer_measure( const TableCtx * ctx, const Layer * root ) +static SCHEMA_UNUSED const TableNodeType * schema_graphdemo_layer_node_lookup_(uint64_t id) { - return root != NULL ? layer_measure_body( ctx, root, 1 ) : -1; + switch (id) { + case UINT64_C(0xf60ec899a5a69fa9): return &schema_graphdemo_list_node_node_type_; + default: return NULL; + } } - -static SCHEMA_UNUSED int64_t layer_save( const TableCtx * ctx, const Layer * root, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int64_t layer_measure_with_allocator(const TableCtx * ctx, const Layer * root, TableAllocator allocator) { - TableWriter w; - if ( root == NULL ) { return -1; } - w = table_writer_make( buffer, capacity ); - if ( !layer_save_body( ctx, &w, root, 1 ) ) { return -1; } - return w.offset; /* == layer_measure( ctx, root ) */ + return table_graph_save(ctx,root,&schema_graphdemo_layer_node_type_,NULL,INT64_MAX,allocator); } - -/* layer_load_measure: the exact region bytes a wire buffer will need. The - caller owns the allocation — generated load code allocates nothing. */ -static SCHEMA_UNUSED int64_t layer_load_measure( const uint8_t * wire, int64_t wire_bytes ) +static SCHEMA_UNUSED int64_t layer_measure(const TableCtx * ctx,const Layer * root) +{ return layer_measure_with_allocator(ctx,root,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t layer_save_with_allocator(const TableCtx * ctx, const Layer * root, uint8_t * buffer, int64_t capacity, TableAllocator allocator) { - TableReport ignored; - TableReader r; - int64_t below; - memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( wire, wire_bytes, &ignored ); - below = layer_load_measure_body( &r, 1 ); - if ( below < 0 ) { below = 0; } - return table_align_up64( (int64_t) sizeof( Layer ) ) + below; -} - -/* layer_load: decode the tolerant wire into the caller's exact-sized region and - return the root. Partial results are kept, as everywhere on this wire — - the report says what happened. NULL means the CALLER's buffer was wrong. */ -static SCHEMA_UNUSED const Layer * layer_load( uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - TableRegionSink region_sink; - TableSink sink; - Layer * root; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - if ( region == NULL || region_bytes < (int64_t) sizeof( Layer ) ) { out->malformed = 1; return NULL; } - if ( ( ( (uintptr_t) region ) & ( kTableAlign - 1 ) ) != 0 ) { out->malformed = 1; return NULL; } - memset( region, 0, (size_t) region_bytes ); - region_sink.base = region; - region_sink.capacity = region_bytes; - region_sink.used = table_align_up64( (int64_t) sizeof( Layer ) ); - sink.region = ®ion_sink; - sink.worker = NULL; - root = (Layer *) (void *) region; - r = table_reader_make( wire, wire_bytes, out ); - layer_load_body( &r, &sink, root, 1 ); - return root; -} - -/* layer_load_builder: the TOOL's path — the same tolerant decode into a fresh - builder, so loaded data can be edited and locked again. */ -static SCHEMA_UNUSED int layer_load_builder( LayerBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - Layer * root; - TableSink sink; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - root = layer_builder_root( builder ); - if ( root == NULL ) { out->malformed = 1; return 0; } - sink.region = NULL; - sink.worker = &builder->main; - r = table_reader_make( wire, wire_bytes, out ); - return layer_load_body( &r, &sink, root, 1 ); + if (buffer==NULL || capacity<0) { return -1; } + return table_graph_save(ctx,root,&schema_graphdemo_layer_node_type_,buffer,capacity,allocator); +} +static SCHEMA_UNUSED int64_t layer_save(const TableCtx * ctx,const Layer * root, uint8_t * buffer, int64_t capacity) +{ return layer_save_with_allocator(ctx,root,buffer,capacity,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t schema_graphdemo_layer_load_layout_(const TableReader * reader, int64_t * data_out, int64_t * records_out) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id; + int64_t data=schema_graphdemo_layer_wire_storage_(reader), records=0; + if(data<0) { return -1; } + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_layer_node_lookup_(id); + if (type!=NULL) { int64_t size=table_node_wire_storage(type,&body); if (size<0 || size>INT64_MAX-data) { return -1; } data+=size; } + records++; + } + if (records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1 || (records+1)*(int64_t)sizeof(TableNodeDirEntry)>INT64_MAX-data) { return -1; } + *data_out=data; *records_out=records; return data+(records+1)*(int64_t)sizeof(TableNodeDirEntry); +} +static SCHEMA_UNUSED int64_t layer_load_measure_ex(const uint8_t * wire, int64_t bytes, int64_t * attribution, TableRefuseReason * reason) +{ + TableReport report; TableReader reader; int64_t data,records,total; memset(&report,0,sizeof(report)); + if (attribution!=NULL) { *attribution=0; } + if (!table_wire_open(&reader,wire,bytes,&report)) { if(report.refused && reason!=NULL) { *reason=SCHEMA_TABLE_REFUSE_UNKNOWN_FORM; } return -1; } + report.reason=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH; total=schema_graphdemo_layer_load_layout_(&reader,&data,&records); + if(total<0 && reason!=NULL) { *reason=report.reason; } + if(total>=0 && attribution!=NULL) { *attribution=(records+1)*(int64_t)sizeof(TableNodeDirEntry); } return total; +} +static SCHEMA_UNUSED int64_t layer_load_measure(const uint8_t * wire, int64_t bytes) +{ return layer_load_measure_ex(wire,bytes,NULL,NULL); } +static SCHEMA_UNUSED int schema_graphdemo_layer_load_graph_(TableReader * reader, Layer * root, TableNodeMap * nodes, TableNodeDirEntry * directory, TableSink * sink) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id,k=0; int32_t unknown=0; + layer_reset(root); + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_layer_node_lookup_(id); + directory[k+1].type_id=id; directory[k+1].offset=UINT64_MAX; + if (type!=NULL && type->blob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; k++; continue; } + if (type==NULL) { unknown++; } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; return 0; } + directory[k+1].offset=(uint64_t)at; sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; return 0; } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; } else { reader->report->malformed=1; } + if(nodes->good) { + scan=table_node_scan_begin(reader); k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; TableSink carve; const TableNodeType * type=schema_graphdemo_layer_node_lookup_(id); + carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; extent.used=type->size; extent.capacity=table_node_wire_storage(type,&body); carve.region=&extent; } + nodes->sink=&carve; body.nodes=nodes; + switch(id) { + case UINT64_C(0xf60ec899a5a69fa9): list_node_load_body(&body,(ListNode *)value); break; + default: break; + } + nodes->sink=NULL; if(nodes->refused) { return 0; } + } + k++; + } + } + reader->nodes=nodes; reader->nested=0; + { int read_ok; TableRegionSink extent; TableSink carve; carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; extent.used=sizeof(*root); extent.capacity=schema_graphdemo_layer_wire_storage_(reader); carve.region=&extent; } + nodes->sink=&carve; read_ok=layer_load_body(reader,root); nodes->sink=NULL; return !nodes->refused && (!nodes->arena || read_ok); } +} +static SCHEMA_UNUSED const Layer * layer_load(uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableRegionSink place; TableSink sink; int64_t data,records,total; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return NULL; } + total=schema_graphdemo_layer_load_layout_(&reader,&data,&records); + if(total<0 || region==NULL || region_bytesmalformed=1; return NULL; } + memset(region,0,(size_t)total); directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; directory[0].type_id=UINT64_C(0x9d167ef77aed79b6); + nodes.base=region; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=0; nodes.refused=0; nodes.sink=NULL; + place.base=region; place.capacity=data; place.used=schema_graphdemo_layer_wire_storage_(&reader); sink.region=&place; sink.worker=NULL; + if(!schema_graphdemo_layer_load_graph_(&reader,(Layer *)(void *)region,&nodes,directory,&sink)) { return NULL; } + return (const Layer *)(const void *)region; +} +static SCHEMA_UNUSED int layer_load_builder(LayerBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableSink sink; TableNodeScan scan; TableReader body; uint64_t id; int64_t records=0; Layer * root; int ok; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return 0; } + scan=table_node_scan_begin(&reader); while(table_node_scan_next(&scan,&id,&body)) { records++; } + if(records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1) { report->malformed=1; return 0; } + root=layer_builder_root(builder); if(root==NULL) { report->malformed=1; return 0; } + directory=(TableNodeDirEntry *)table_allocate(builder->arena.allocator,(records+1)*(int64_t)sizeof(TableNodeDirEntry)); + if(directory==NULL) { report->malformed=1; return 0; } + directory[0].offset=(uint64_t)builder->root_ref.value; directory[0].type_id=UINT64_C(0x9d167ef77aed79b6); + nodes.base=NULL; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=1; nodes.refused=0; nodes.sink=NULL; sink.region=NULL; sink.worker=&builder->main; + ok=schema_graphdemo_layer_load_graph_(&reader,root,&nodes,directory,&sink); table_release(builder->arena.allocator,directory); return ok; +} +static SCHEMA_UNUSED int schema_graphdemo_layer_message_graph_save_(TableBitWriter * w,const Layer * root,TableAllocator allocator) +{ + TableNumbering numbering; + uint64_t i; + int ok=0; + if(!table_number_build(&numbering,NULL,root,&schema_graphdemo_layer_node_type_,allocator))goto done; + w->nodes=&numbering; + w->index_bits=table_bits_required(0,(int64_t)numbering.count); + if(numbering.count-1>UINT32_MAX)goto done; + if(numbering.count>1){ + table_bit_put(w,37,kTableMessageRefBitsHere); + table_bit_put(w,numbering.count-1,32); + for(i=1;itype->id){ + case UINT64_C(0xf60ec899a5a69fa9): + table_bit_put(w,44,kTableMessageRefBitsHere); + if(!list_node_save_message_body(w,(const ListNode *)node->value))goto done; + break; + default:goto done; + } } }ok=layer_save_message_body(w,root); + done:table_number_dispose(&numbering); + w->nodes=NULL; + return ok&&!w->overflow; +} +static SCHEMA_UNUSED int64_t layer_measure_messages_with_allocator(const Layer * const * roots,int64_t count,TableReport * report,TableAllocator allocator) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(roots==NULL||count<1)return -1; + if(count>kTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL||capacity<0)return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid; + if(*id==UINT64_C(0x2f2ec0474f1c4fe4)||*id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_message_blob_scan(r,length))return 0; + return *size>=0; + }switch(*id){ + case UINT64_C(0xf60ec899a5a69fa9):at=sizeof(ListNode); + if(!schema_graphdemo_list_node_message_extent_(r,&at))return 0; + break; + default:return table_message_skip_body(r); + }if(at>INT64_MAX-kTableAlign)return 0; + *size=table_align_up64(at); + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_layer_message_storage_(TableMessageReader * r,uint64_t * records,int64_t * data,int64_t * root_bytes,int * complete) +{ + uint64_t i,id,length; + int64_t size,root=sizeof(Layer); + *data=0; + *complete=1; + if(!table_message_nodes_open(r,records))return 0; + for(i=0;i<*records;i++){ + if(!schema_graphdemo_layer_message_record_scan_(r,&id,&size,&length)||size>INT64_MAX-*data)return 0; + *data+=size; + } + if(!schema_graphdemo_layer_message_extent_(r,&root))*complete=0; + if(r->extent_refused)return 0; + if(root>INT64_MAX-kTableAlign)return 0; + *root_bytes=table_align_up64(root); + if(*root_bytes>INT64_MAX-*data)return 0; + *data+=*root_bytes; + return 1; +} +static SCHEMA_UNUSED int64_t layer_load_measure_messages_ex(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,int64_t * attribution,TableRefuseReason * reason) +{ + TableReport report={0}; + TableMessageReader r; + int64_t total=0,dirs=0,bodies,i; + if(attribution)*attribution=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,&report); + if(bodies<0)return -1; + for(i=0;i=(uint64_t)(INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)))return -1; + dir=(int64_t)(records+1)*(int64_t)sizeof(TableNodeDirEntry); + if(data>INT64_MAX-dir || data+dir>INT64_MAX-total)return -1; + total+=data+dir; + dirs+=dir; + if(!complete)break; + }if(attribution)*attribution=dirs; + return total; +} +static SCHEMA_UNUSED int64_t layer_load_measure_messages(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes) +{ return layer_load_measure_messages_ex(vocabulary,buffer,bytes,NULL,NULL); +} +static SCHEMA_UNUSED int schema_graphdemo_layer_message_load_into_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Layer ** out) +{ + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Layer); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Layer * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Layer *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0x9d167ef77aed79b6); + *used+=root_size; + layer_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body(r,(ListNode *)(void *)value))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=layer_load_message_body(r,root); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int layer_load_messages(const Layer ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t bodies,capacity,used=0,i; + if(report==NULL)report=&ignored; + if(roots==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(region==NULL||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iarena ); + table_arena_init_with_allocator( &builder->arena, allocator ); builder->main = table_worker_make( &builder->arena ); builder->root_ref.value = 0; builder->region = NULL; @@ -4453,10 +11301,12 @@ static SCHEMA_UNUSED int scene_builder_init( SceneBuilder * builder ) return 1; } +static SCHEMA_UNUSED int scene_builder_init(SceneBuilder * builder) { return scene_builder_init_with_allocator(builder,table_default_allocator()); } + static SCHEMA_UNUSED void scene_builder_shutdown( SceneBuilder * builder ) { table_arena_shutdown( &builder->arena ); - free( builder->region ); + table_release(builder->arena.allocator,builder->region); builder->region = NULL; builder->region_bytes = 0; } @@ -4468,119 +11318,405 @@ static SCHEMA_UNUSED Scene * scene_builder_root( SceneBuilder * builder ) return (Scene *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); } -/* Lock is ONE WAY and it is the compaction: the segmented arena becomes one - exact-packed region with zero slack, references rewritten self-relative, - and the mutable life released. Single-threaded: call it after the workers - have joined. The region comes back in builder->region. */ -static SCHEMA_UNUSED int scene_builder_lock( SceneBuilder * builder ) -{ - TableCtx ctx; - const Scene * root; - int64_t below, total, used; - uint8_t * packed; - if ( builder->arena.locked ) { return builder->region != NULL; } - if ( builder->root_ref.value == 0 ) { return 0; } - ctx.arena = &builder->arena; - root = (const Scene *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); - below = scene_pack_measure( &ctx, root, 1 ); - if ( below < 0 ) { return 0; } /* a data cycle, or a chain past kTableMaxDepth */ - total = table_align_up64( (int64_t) sizeof( Scene ) ) + below; - packed = (uint8_t *) malloc( (size_t) total ); /* the AUTHORING path may allocate */ - if ( packed == NULL ) { return 0; } - memset( packed, 0, (size_t) total ); - used = table_align_up64( (int64_t) sizeof( Scene ) ); - if ( !scene_pack( &ctx, root, (Scene *) (void *) packed, packed, total, &used, 1 ) || used != total ) - { - free( packed ); - return 0; - } - builder->region = packed; - builder->region_bytes = total; - builder->arena.locked = 1; /* MONOTONIC: there is no unlock */ - table_arena_shutdown( &builder->arena ); - return 1; +static SCHEMA_UNUSED int scene_builder_lock(SceneBuilder * builder) +{ + TableCtx ctx; const Scene * root; uint8_t * packed; int64_t bytes; + if (builder->arena.locked) { return builder->region!=NULL; } + if (builder->root_ref.value==0) { return 0; } + ctx.arena=&builder->arena; root=(const Scene *)table_arena_at(&builder->arena,(uint32_t)builder->root_ref.value); + packed=table_graph_pack(&ctx,root,&schema_graphdemo_scene_node_type_,&bytes); + if (packed==NULL) { return 0; } + builder->region=packed; builder->region_bytes=bytes; builder->arena.locked=1; + table_arena_shutdown(&builder->arena); return 1; } -/* ---- Scene on the wire: the generic, tolerant form (docs/SPEC-TABLES.md §3) ---- - - The CONTEXT says which form the root is in: NULL, or a ctx with a NULL - arena, is a packed REGION — what Lock and Load produce — and a ctx naming - a builder's arena is the mutable form. C++ spells the two as overloads; - one parameter says the same thing here. */ -static SCHEMA_UNUSED int64_t scene_measure( const TableCtx * ctx, const Scene * root ) +static SCHEMA_UNUSED const TableNodeType * schema_graphdemo_scene_node_lookup_(uint64_t id) { - return root != NULL ? scene_measure_body( ctx, root, 1 ) : -1; + switch (id) { + case UINT64_C(0xf60ec899a5a69fa9): return &schema_graphdemo_list_node_node_type_; + case UINT64_C(0xb97e90a3784c431d): return &schema_graphdemo_tree_node_node_type_; + case UINT64_C(0x9d8b8aa2b404c2c8): return &schema_graphdemo_settings_node_type_; + default: return NULL; + } } - -static SCHEMA_UNUSED int64_t scene_save( const TableCtx * ctx, const Scene * root, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int64_t scene_measure_with_allocator(const TableCtx * ctx, const Scene * root, TableAllocator allocator) { - TableWriter w; - if ( root == NULL ) { return -1; } - w = table_writer_make( buffer, capacity ); - if ( !scene_save_body( ctx, &w, root, 1 ) ) { return -1; } - return w.offset; /* == scene_measure( ctx, root ) */ + return table_graph_save(ctx,root,&schema_graphdemo_scene_node_type_,NULL,INT64_MAX,allocator); } - -/* scene_load_measure: the exact region bytes a wire buffer will need. The - caller owns the allocation — generated load code allocates nothing. */ -static SCHEMA_UNUSED int64_t scene_load_measure( const uint8_t * wire, int64_t wire_bytes ) +static SCHEMA_UNUSED int64_t scene_measure(const TableCtx * ctx,const Scene * root) +{ return scene_measure_with_allocator(ctx,root,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t scene_save_with_allocator(const TableCtx * ctx, const Scene * root, uint8_t * buffer, int64_t capacity, TableAllocator allocator) { - TableReport ignored; - TableReader r; - int64_t below; - memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( wire, wire_bytes, &ignored ); - below = scene_load_measure_body( &r, 1 ); - if ( below < 0 ) { below = 0; } - return table_align_up64( (int64_t) sizeof( Scene ) ) + below; -} - -/* scene_load: decode the tolerant wire into the caller's exact-sized region and - return the root. Partial results are kept, as everywhere on this wire — - the report says what happened. NULL means the CALLER's buffer was wrong. */ -static SCHEMA_UNUSED const Scene * scene_load( uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - TableRegionSink region_sink; - TableSink sink; - Scene * root; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - if ( region == NULL || region_bytes < (int64_t) sizeof( Scene ) ) { out->malformed = 1; return NULL; } - if ( ( ( (uintptr_t) region ) & ( kTableAlign - 1 ) ) != 0 ) { out->malformed = 1; return NULL; } - memset( region, 0, (size_t) region_bytes ); - region_sink.base = region; - region_sink.capacity = region_bytes; - region_sink.used = table_align_up64( (int64_t) sizeof( Scene ) ); - sink.region = ®ion_sink; - sink.worker = NULL; - root = (Scene *) (void *) region; - r = table_reader_make( wire, wire_bytes, out ); - scene_load_body( &r, &sink, root, 1 ); - return root; -} - -/* scene_load_builder: the TOOL's path — the same tolerant decode into a fresh - builder, so loaded data can be edited and locked again. */ -static SCHEMA_UNUSED int scene_load_builder( SceneBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - Scene * root; - TableSink sink; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - root = scene_builder_root( builder ); - if ( root == NULL ) { out->malformed = 1; return 0; } - sink.region = NULL; - sink.worker = &builder->main; - r = table_reader_make( wire, wire_bytes, out ); - return scene_load_body( &r, &sink, root, 1 ); + if (buffer==NULL || capacity<0) { return -1; } + return table_graph_save(ctx,root,&schema_graphdemo_scene_node_type_,buffer,capacity,allocator); +} +static SCHEMA_UNUSED int64_t scene_save(const TableCtx * ctx,const Scene * root, uint8_t * buffer, int64_t capacity) +{ return scene_save_with_allocator(ctx,root,buffer,capacity,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t schema_graphdemo_scene_load_layout_(const TableReader * reader, int64_t * data_out, int64_t * records_out) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id; + int64_t data=schema_graphdemo_scene_wire_storage_(reader), records=0; + if(data<0) { return -1; } + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_scene_node_lookup_(id); + if (type!=NULL) { int64_t size=table_node_wire_storage(type,&body); if (size<0 || size>INT64_MAX-data) { return -1; } data+=size; } + records++; + } + if (records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1 || (records+1)*(int64_t)sizeof(TableNodeDirEntry)>INT64_MAX-data) { return -1; } + *data_out=data; *records_out=records; return data+(records+1)*(int64_t)sizeof(TableNodeDirEntry); +} +static SCHEMA_UNUSED int64_t scene_load_measure_ex(const uint8_t * wire, int64_t bytes, int64_t * attribution, TableRefuseReason * reason) +{ + TableReport report; TableReader reader; int64_t data,records,total; memset(&report,0,sizeof(report)); + if (attribution!=NULL) { *attribution=0; } + if (!table_wire_open(&reader,wire,bytes,&report)) { if(report.refused && reason!=NULL) { *reason=SCHEMA_TABLE_REFUSE_UNKNOWN_FORM; } return -1; } + report.reason=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH; total=schema_graphdemo_scene_load_layout_(&reader,&data,&records); + if(total<0 && reason!=NULL) { *reason=report.reason; } + if(total>=0 && attribution!=NULL) { *attribution=(records+1)*(int64_t)sizeof(TableNodeDirEntry); } return total; +} +static SCHEMA_UNUSED int64_t scene_load_measure(const uint8_t * wire, int64_t bytes) +{ return scene_load_measure_ex(wire,bytes,NULL,NULL); } +static SCHEMA_UNUSED int schema_graphdemo_scene_load_graph_(TableReader * reader, Scene * root, TableNodeMap * nodes, TableNodeDirEntry * directory, TableSink * sink) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id,k=0; int32_t unknown=0; + scene_reset(root); + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_scene_node_lookup_(id); + directory[k+1].type_id=id; directory[k+1].offset=UINT64_MAX; + if (type!=NULL && type->blob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; k++; continue; } + if (type==NULL) { unknown++; } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; return 0; } + directory[k+1].offset=(uint64_t)at; sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; return 0; } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; } else { reader->report->malformed=1; } + if(nodes->good) { + scan=table_node_scan_begin(reader); k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; TableSink carve; const TableNodeType * type=schema_graphdemo_scene_node_lookup_(id); + carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; extent.used=type->size; extent.capacity=table_node_wire_storage(type,&body); carve.region=&extent; } + nodes->sink=&carve; body.nodes=nodes; + switch(id) { + case UINT64_C(0xf60ec899a5a69fa9): list_node_load_body(&body,(ListNode *)value); break; + case UINT64_C(0xb97e90a3784c431d): tree_node_load_body(&body,(TreeNode *)value); break; + case UINT64_C(0x9d8b8aa2b404c2c8): settings_load_body(&body,(Settings *)value); break; + default: break; + } + nodes->sink=NULL; if(nodes->refused) { return 0; } + } + k++; + } + } + reader->nodes=nodes; reader->nested=0; + { int read_ok; TableRegionSink extent; TableSink carve; carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; extent.used=sizeof(*root); extent.capacity=schema_graphdemo_scene_wire_storage_(reader); carve.region=&extent; } + nodes->sink=&carve; read_ok=scene_load_body(reader,root); nodes->sink=NULL; return !nodes->refused && (!nodes->arena || read_ok); } +} +static SCHEMA_UNUSED const Scene * scene_load(uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableRegionSink place; TableSink sink; int64_t data,records,total; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return NULL; } + total=schema_graphdemo_scene_load_layout_(&reader,&data,&records); + if(total<0 || region==NULL || region_bytesmalformed=1; return NULL; } + memset(region,0,(size_t)total); directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; directory[0].type_id=UINT64_C(0x4a9a31623ab5f213); + nodes.base=region; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=0; nodes.refused=0; nodes.sink=NULL; + place.base=region; place.capacity=data; place.used=schema_graphdemo_scene_wire_storage_(&reader); sink.region=&place; sink.worker=NULL; + if(!schema_graphdemo_scene_load_graph_(&reader,(Scene *)(void *)region,&nodes,directory,&sink)) { return NULL; } + return (const Scene *)(const void *)region; +} +static SCHEMA_UNUSED int scene_load_builder(SceneBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableSink sink; TableNodeScan scan; TableReader body; uint64_t id; int64_t records=0; Scene * root; int ok; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return 0; } + scan=table_node_scan_begin(&reader); while(table_node_scan_next(&scan,&id,&body)) { records++; } + if(records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1) { report->malformed=1; return 0; } + root=scene_builder_root(builder); if(root==NULL) { report->malformed=1; return 0; } + directory=(TableNodeDirEntry *)table_allocate(builder->arena.allocator,(records+1)*(int64_t)sizeof(TableNodeDirEntry)); + if(directory==NULL) { report->malformed=1; return 0; } + directory[0].offset=(uint64_t)builder->root_ref.value; directory[0].type_id=UINT64_C(0x4a9a31623ab5f213); + nodes.base=NULL; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=1; nodes.refused=0; nodes.sink=NULL; sink.region=NULL; sink.worker=&builder->main; + ok=schema_graphdemo_scene_load_graph_(&reader,root,&nodes,directory,&sink); table_release(builder->arena.allocator,directory); return ok; +} +static SCHEMA_UNUSED int schema_graphdemo_scene_message_graph_save_(TableBitWriter * w,const Scene * root,TableAllocator allocator) +{ + TableNumbering numbering; + uint64_t i; + int ok=0; + if(!table_number_build(&numbering,NULL,root,&schema_graphdemo_scene_node_type_,allocator))goto done; + w->nodes=&numbering; + w->index_bits=table_bits_required(0,(int64_t)numbering.count); + if(numbering.count-1>UINT32_MAX)goto done; + if(numbering.count>1){ + table_bit_put(w,37,kTableMessageRefBitsHere); + table_bit_put(w,numbering.count-1,32); + for(i=1;itype->id){ + case UINT64_C(0xf60ec899a5a69fa9): + table_bit_put(w,44,kTableMessageRefBitsHere); + if(!list_node_save_message_body(w,(const ListNode *)node->value))goto done; + break; + case UINT64_C(0xb97e90a3784c431d): + table_bit_put(w,51,kTableMessageRefBitsHere); + if(!tree_node_save_message_body(w,(const TreeNode *)node->value))goto done; + break; + case UINT64_C(0x9d8b8aa2b404c2c8): + table_bit_put(w,48,kTableMessageRefBitsHere); + if(!settings_save_message_body(w,(const Settings *)node->value))goto done; + break; + default:goto done; + } } }ok=scene_save_message_body(w,root); + done:table_number_dispose(&numbering); + w->nodes=NULL; + return ok&&!w->overflow; +} +static SCHEMA_UNUSED int64_t scene_measure_messages_with_allocator(const Scene * const * roots,int64_t count,TableReport * report,TableAllocator allocator) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(roots==NULL||count<1)return -1; + if(count>kTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL||capacity<0)return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid; + if(*id==UINT64_C(0x2f2ec0474f1c4fe4)||*id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_message_blob_scan(r,length))return 0; + return *size>=0; + }switch(*id){ + case UINT64_C(0xf60ec899a5a69fa9):at=sizeof(ListNode); + if(!schema_graphdemo_list_node_message_extent_(r,&at))return 0; + break; + case UINT64_C(0xb97e90a3784c431d):at=sizeof(TreeNode); + if(!schema_graphdemo_tree_node_message_extent_(r,&at))return 0; + break; + case UINT64_C(0x9d8b8aa2b404c2c8):at=sizeof(Settings); + if(!schema_graphdemo_settings_message_extent_(r,&at))return 0; + break; + default:return table_message_skip_body(r); + }if(at>INT64_MAX-kTableAlign)return 0; + *size=table_align_up64(at); + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_scene_message_storage_(TableMessageReader * r,uint64_t * records,int64_t * data,int64_t * root_bytes,int * complete) +{ + uint64_t i,id,length; + int64_t size,root=sizeof(Scene); + *data=0; + *complete=1; + if(!table_message_nodes_open(r,records))return 0; + for(i=0;i<*records;i++){ + if(!schema_graphdemo_scene_message_record_scan_(r,&id,&size,&length)||size>INT64_MAX-*data)return 0; + *data+=size; + } + if(!schema_graphdemo_scene_message_extent_(r,&root))*complete=0; + if(r->extent_refused)return 0; + if(root>INT64_MAX-kTableAlign)return 0; + *root_bytes=table_align_up64(root); + if(*root_bytes>INT64_MAX-*data)return 0; + *data+=*root_bytes; + return 1; +} +static SCHEMA_UNUSED int64_t scene_load_measure_messages_ex(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,int64_t * attribution,TableRefuseReason * reason) +{ + TableReport report={0}; + TableMessageReader r; + int64_t total=0,dirs=0,bodies,i; + if(attribution)*attribution=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,&report); + if(bodies<0)return -1; + for(i=0;i=(uint64_t)(INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)))return -1; + dir=(int64_t)(records+1)*(int64_t)sizeof(TableNodeDirEntry); + if(data>INT64_MAX-dir || data+dir>INT64_MAX-total)return -1; + total+=data+dir; + dirs+=dir; + if(!complete)break; + }if(attribution)*attribution=dirs; + return total; +} +static SCHEMA_UNUSED int64_t scene_load_measure_messages(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes) +{ return scene_load_measure_messages_ex(vocabulary,buffer,bytes,NULL,NULL); +} +static SCHEMA_UNUSED int schema_graphdemo_scene_message_load_into_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Scene ** out) +{ + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Scene); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Scene * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Scene *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0x4a9a31623ab5f213); + *used+=root_size; + scene_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body(r,(ListNode *)(void *)value))goto malformed; + break; + case UINT64_C(0xb97e90a3784c431d):carve.used=sizeof(TreeNode); + carve.capacity=sizeof(TreeNode); + scan=*r; + if(!schema_graphdemo_tree_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!tree_node_load_message_body(r,(TreeNode *)(void *)value))goto malformed; + break; + case UINT64_C(0x9d8b8aa2b404c2c8):carve.used=sizeof(Settings); + carve.capacity=sizeof(Settings); + scan=*r; + if(!schema_graphdemo_settings_message_extent_(&scan,&carve.capacity))goto malformed; + if(!settings_load_message_body(r,(Settings *)(void *)value))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=scene_load_message_body(r,root); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int scene_load_messages(const Scene ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t bodies,capacity,used=0,i; + if(report==NULL)report=&ignored; + if(roots==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(region==NULL||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iarena ); + table_arena_init_with_allocator( &builder->arena, allocator ); builder->main = table_worker_make( &builder->arena ); builder->root_ref.value = 0; builder->region = NULL; @@ -4625,10 +11761,12 @@ static SCHEMA_UNUSED int depot_builder_init( DepotBuilder * builder ) return 1; } +static SCHEMA_UNUSED int depot_builder_init(DepotBuilder * builder) { return depot_builder_init_with_allocator(builder,table_default_allocator()); } + static SCHEMA_UNUSED void depot_builder_shutdown( DepotBuilder * builder ) { table_arena_shutdown( &builder->arena ); - free( builder->region ); + table_release(builder->arena.allocator,builder->region); builder->region = NULL; builder->region_bytes = 0; } @@ -4640,119 +11778,375 @@ static SCHEMA_UNUSED Depot * depot_builder_root( DepotBuilder * builder ) return (Depot *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); } -/* Lock is ONE WAY and it is the compaction: the segmented arena becomes one - exact-packed region with zero slack, references rewritten self-relative, - and the mutable life released. Single-threaded: call it after the workers - have joined. The region comes back in builder->region. */ -static SCHEMA_UNUSED int depot_builder_lock( DepotBuilder * builder ) -{ - TableCtx ctx; - const Depot * root; - int64_t below, total, used; - uint8_t * packed; - if ( builder->arena.locked ) { return builder->region != NULL; } - if ( builder->root_ref.value == 0 ) { return 0; } - ctx.arena = &builder->arena; - root = (const Depot *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); - below = depot_pack_measure( &ctx, root, 1 ); - if ( below < 0 ) { return 0; } /* a data cycle, or a chain past kTableMaxDepth */ - total = table_align_up64( (int64_t) sizeof( Depot ) ) + below; - packed = (uint8_t *) malloc( (size_t) total ); /* the AUTHORING path may allocate */ - if ( packed == NULL ) { return 0; } - memset( packed, 0, (size_t) total ); - used = table_align_up64( (int64_t) sizeof( Depot ) ); - if ( !depot_pack( &ctx, root, (Depot *) (void *) packed, packed, total, &used, 1 ) || used != total ) - { - free( packed ); - return 0; - } - builder->region = packed; - builder->region_bytes = total; - builder->arena.locked = 1; /* MONOTONIC: there is no unlock */ - table_arena_shutdown( &builder->arena ); - return 1; +static SCHEMA_UNUSED int depot_builder_lock(DepotBuilder * builder) +{ + TableCtx ctx; const Depot * root; uint8_t * packed; int64_t bytes; + if (builder->arena.locked) { return builder->region!=NULL; } + if (builder->root_ref.value==0) { return 0; } + ctx.arena=&builder->arena; root=(const Depot *)table_arena_at(&builder->arena,(uint32_t)builder->root_ref.value); + packed=table_graph_pack(&ctx,root,&schema_graphdemo_depot_node_type_,&bytes); + if (packed==NULL) { return 0; } + builder->region=packed; builder->region_bytes=bytes; builder->arena.locked=1; + table_arena_shutdown(&builder->arena); return 1; } -/* ---- Depot on the wire: the generic, tolerant form (docs/SPEC-TABLES.md §3) ---- - - The CONTEXT says which form the root is in: NULL, or a ctx with a NULL - arena, is a packed REGION — what Lock and Load produce — and a ctx naming - a builder's arena is the mutable form. C++ spells the two as overloads; - one parameter says the same thing here. */ -static SCHEMA_UNUSED int64_t depot_measure( const TableCtx * ctx, const Depot * root ) +static SCHEMA_UNUSED const TableNodeType * schema_graphdemo_depot_node_lookup_(uint64_t id) { - return root != NULL ? depot_measure_body( ctx, root, 1 ) : -1; + switch (id) { + case UINT64_C(0xf60ec899a5a69fa9): return &schema_graphdemo_list_node_node_type_; + default: return NULL; + } } - -static SCHEMA_UNUSED int64_t depot_save( const TableCtx * ctx, const Depot * root, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int64_t depot_measure_with_allocator(const TableCtx * ctx, const Depot * root, TableAllocator allocator) { - TableWriter w; - if ( root == NULL ) { return -1; } - w = table_writer_make( buffer, capacity ); - if ( !depot_save_body( ctx, &w, root, 1 ) ) { return -1; } - return w.offset; /* == depot_measure( ctx, root ) */ + return table_graph_save(ctx,root,&schema_graphdemo_depot_node_type_,NULL,INT64_MAX,allocator); } - -/* depot_load_measure: the exact region bytes a wire buffer will need. The - caller owns the allocation — generated load code allocates nothing. */ -static SCHEMA_UNUSED int64_t depot_load_measure( const uint8_t * wire, int64_t wire_bytes ) +static SCHEMA_UNUSED int64_t depot_measure(const TableCtx * ctx,const Depot * root) +{ return depot_measure_with_allocator(ctx,root,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t depot_save_with_allocator(const TableCtx * ctx, const Depot * root, uint8_t * buffer, int64_t capacity, TableAllocator allocator) { - TableReport ignored; - TableReader r; - int64_t below; - memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( wire, wire_bytes, &ignored ); - below = depot_load_measure_body( &r, 1 ); - if ( below < 0 ) { below = 0; } - return table_align_up64( (int64_t) sizeof( Depot ) ) + below; -} - -/* depot_load: decode the tolerant wire into the caller's exact-sized region and - return the root. Partial results are kept, as everywhere on this wire — - the report says what happened. NULL means the CALLER's buffer was wrong. */ -static SCHEMA_UNUSED const Depot * depot_load( uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - TableRegionSink region_sink; - TableSink sink; - Depot * root; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - if ( region == NULL || region_bytes < (int64_t) sizeof( Depot ) ) { out->malformed = 1; return NULL; } - if ( ( ( (uintptr_t) region ) & ( kTableAlign - 1 ) ) != 0 ) { out->malformed = 1; return NULL; } - memset( region, 0, (size_t) region_bytes ); - region_sink.base = region; - region_sink.capacity = region_bytes; - region_sink.used = table_align_up64( (int64_t) sizeof( Depot ) ); - sink.region = ®ion_sink; - sink.worker = NULL; - root = (Depot *) (void *) region; - r = table_reader_make( wire, wire_bytes, out ); - depot_load_body( &r, &sink, root, 1 ); - return root; -} - -/* depot_load_builder: the TOOL's path — the same tolerant decode into a fresh - builder, so loaded data can be edited and locked again. */ -static SCHEMA_UNUSED int depot_load_builder( DepotBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - Depot * root; - TableSink sink; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - root = depot_builder_root( builder ); - if ( root == NULL ) { out->malformed = 1; return 0; } - sink.region = NULL; - sink.worker = &builder->main; - r = table_reader_make( wire, wire_bytes, out ); - return depot_load_body( &r, &sink, root, 1 ); + if (buffer==NULL || capacity<0) { return -1; } + return table_graph_save(ctx,root,&schema_graphdemo_depot_node_type_,buffer,capacity,allocator); +} +static SCHEMA_UNUSED int64_t depot_save(const TableCtx * ctx,const Depot * root, uint8_t * buffer, int64_t capacity) +{ return depot_save_with_allocator(ctx,root,buffer,capacity,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t schema_graphdemo_depot_load_layout_(const TableReader * reader, int64_t * data_out, int64_t * records_out) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id; + int64_t data=schema_graphdemo_depot_wire_storage_(reader), records=0; + if(data<0) { return -1; } + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_depot_node_lookup_(id); + if (type!=NULL) { int64_t size=table_node_wire_storage(type,&body); if (size<0 || size>INT64_MAX-data) { return -1; } data+=size; } + records++; + } + if (records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1 || (records+1)*(int64_t)sizeof(TableNodeDirEntry)>INT64_MAX-data) { return -1; } + *data_out=data; *records_out=records; return data+(records+1)*(int64_t)sizeof(TableNodeDirEntry); +} +static SCHEMA_UNUSED int64_t depot_load_measure_ex(const uint8_t * wire, int64_t bytes, int64_t * attribution, TableRefuseReason * reason) +{ + TableReport report; TableReader reader; int64_t data,records,total; memset(&report,0,sizeof(report)); + if (attribution!=NULL) { *attribution=0; } + if (!table_wire_open(&reader,wire,bytes,&report)) { if(report.refused && reason!=NULL) { *reason=SCHEMA_TABLE_REFUSE_UNKNOWN_FORM; } return -1; } + report.reason=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH; total=schema_graphdemo_depot_load_layout_(&reader,&data,&records); + if(total<0 && reason!=NULL) { *reason=report.reason; } + if(total>=0 && attribution!=NULL) { *attribution=(records+1)*(int64_t)sizeof(TableNodeDirEntry); } return total; +} +static SCHEMA_UNUSED int64_t depot_load_measure(const uint8_t * wire, int64_t bytes) +{ return depot_load_measure_ex(wire,bytes,NULL,NULL); } +static SCHEMA_UNUSED int schema_graphdemo_depot_load_graph_(TableReader * reader, Depot * root, TableNodeMap * nodes, TableNodeDirEntry * directory, TableSink * sink) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id,k=0; int32_t unknown=0; + depot_reset(root); + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_depot_node_lookup_(id); + directory[k+1].type_id=id; directory[k+1].offset=UINT64_MAX; + if (type!=NULL && type->blob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; k++; continue; } + if (type==NULL) { unknown++; } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; return 0; } + directory[k+1].offset=(uint64_t)at; sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; return 0; } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; } else { reader->report->malformed=1; } + if(nodes->good) { + scan=table_node_scan_begin(reader); k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; TableSink carve; const TableNodeType * type=schema_graphdemo_depot_node_lookup_(id); + carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; extent.used=type->size; extent.capacity=table_node_wire_storage(type,&body); carve.region=&extent; } + nodes->sink=&carve; body.nodes=nodes; + switch(id) { + case UINT64_C(0xf60ec899a5a69fa9): list_node_load_body(&body,(ListNode *)value); break; + default: break; + } + nodes->sink=NULL; if(nodes->refused) { return 0; } + } + k++; + } + } + reader->nodes=nodes; reader->nested=0; + { int read_ok; TableRegionSink extent; TableSink carve; carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; extent.used=sizeof(*root); extent.capacity=schema_graphdemo_depot_wire_storage_(reader); carve.region=&extent; } + nodes->sink=&carve; read_ok=depot_load_body(reader,root); nodes->sink=NULL; return !nodes->refused && (!nodes->arena || read_ok); } +} +static SCHEMA_UNUSED const Depot * depot_load(uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableRegionSink place; TableSink sink; int64_t data,records,total; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return NULL; } + total=schema_graphdemo_depot_load_layout_(&reader,&data,&records); + if(total<0 || region==NULL || region_bytesmalformed=1; return NULL; } + memset(region,0,(size_t)total); directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; directory[0].type_id=UINT64_C(0x327fe6dc702553fd); + nodes.base=region; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=0; nodes.refused=0; nodes.sink=NULL; + place.base=region; place.capacity=data; place.used=schema_graphdemo_depot_wire_storage_(&reader); sink.region=&place; sink.worker=NULL; + if(!schema_graphdemo_depot_load_graph_(&reader,(Depot *)(void *)region,&nodes,directory,&sink)) { return NULL; } + return (const Depot *)(const void *)region; +} +static SCHEMA_UNUSED int depot_load_builder(DepotBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableSink sink; TableNodeScan scan; TableReader body; uint64_t id; int64_t records=0; Depot * root; int ok; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return 0; } + scan=table_node_scan_begin(&reader); while(table_node_scan_next(&scan,&id,&body)) { records++; } + if(records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1) { report->malformed=1; return 0; } + root=depot_builder_root(builder); if(root==NULL) { report->malformed=1; return 0; } + directory=(TableNodeDirEntry *)table_allocate(builder->arena.allocator,(records+1)*(int64_t)sizeof(TableNodeDirEntry)); + if(directory==NULL) { report->malformed=1; return 0; } + directory[0].offset=(uint64_t)builder->root_ref.value; directory[0].type_id=UINT64_C(0x327fe6dc702553fd); + nodes.base=NULL; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=1; nodes.refused=0; nodes.sink=NULL; sink.region=NULL; sink.worker=&builder->main; + ok=schema_graphdemo_depot_load_graph_(&reader,root,&nodes,directory,&sink); table_release(builder->arena.allocator,directory); return ok; +} +static SCHEMA_UNUSED int schema_graphdemo_depot_message_graph_save_(TableBitWriter * w,const Depot * root,TableAllocator allocator) +{ + TableNumbering numbering; + uint64_t i; + int ok=0; + if(!table_number_build(&numbering,NULL,root,&schema_graphdemo_depot_node_type_,allocator))goto done; + w->nodes=&numbering; + w->index_bits=table_bits_required(0,(int64_t)numbering.count); + if(numbering.count-1>UINT32_MAX)goto done; + if(numbering.count>1){ + table_bit_put(w,37,kTableMessageRefBitsHere); + table_bit_put(w,numbering.count-1,32); + for(i=1;itype->id){ + case UINT64_C(0xf60ec899a5a69fa9): + table_bit_put(w,44,kTableMessageRefBitsHere); + if(!list_node_save_message_body(w,(const ListNode *)node->value))goto done; + break; + default:goto done; + } } }ok=depot_save_message_body(w,root); + done:table_number_dispose(&numbering); + w->nodes=NULL; + return ok&&!w->overflow; +} +static SCHEMA_UNUSED int64_t depot_measure_messages_with_allocator(const Depot * const * roots,int64_t count,TableReport * report,TableAllocator allocator) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(roots==NULL||count<1)return -1; + if(count>kTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL||capacity<0)return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid; + if(*id==UINT64_C(0x2f2ec0474f1c4fe4)||*id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_message_blob_scan(r,length))return 0; + return *size>=0; + }switch(*id){ + case UINT64_C(0xf60ec899a5a69fa9):at=sizeof(ListNode); + if(!schema_graphdemo_list_node_message_extent_(r,&at))return 0; + break; + default:return table_message_skip_body(r); + }if(at>INT64_MAX-kTableAlign)return 0; + *size=table_align_up64(at); + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_depot_message_storage_(TableMessageReader * r,uint64_t * records,int64_t * data,int64_t * root_bytes,int * complete) +{ + uint64_t i,id,length; + int64_t size,root=sizeof(Depot); + *data=0; + *complete=1; + if(!table_message_nodes_open(r,records))return 0; + for(i=0;i<*records;i++){ + if(!schema_graphdemo_depot_message_record_scan_(r,&id,&size,&length)||size>INT64_MAX-*data)return 0; + *data+=size; + } + if(!schema_graphdemo_depot_message_extent_(r,&root))*complete=0; + if(r->extent_refused)return 0; + if(root>INT64_MAX-kTableAlign)return 0; + *root_bytes=table_align_up64(root); + if(*root_bytes>INT64_MAX-*data)return 0; + *data+=*root_bytes; + return 1; +} +static SCHEMA_UNUSED int64_t depot_load_measure_messages_ex(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,int64_t * attribution,TableRefuseReason * reason) +{ + TableReport report={0}; + TableMessageReader r; + int64_t total=0,dirs=0,bodies,i; + if(attribution)*attribution=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,&report); + if(bodies<0)return -1; + for(i=0;i=(uint64_t)(INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)))return -1; + dir=(int64_t)(records+1)*(int64_t)sizeof(TableNodeDirEntry); + if(data>INT64_MAX-dir || data+dir>INT64_MAX-total)return -1; + total+=data+dir; + dirs+=dir; + if(!complete)break; + }if(attribution)*attribution=dirs; + return total; +} +static SCHEMA_UNUSED int64_t depot_load_measure_messages(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes) +{ return depot_load_measure_messages_ex(vocabulary,buffer,bytes,NULL,NULL); +} +static SCHEMA_UNUSED int schema_graphdemo_depot_message_load_into_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Depot ** out) +{ + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Depot); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Depot * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Depot *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0x327fe6dc702553fd); + *used+=root_size; + depot_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body(r,(ListNode *)(void *)value))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=depot_load_message_body(r,root); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int depot_load_messages(const Depot ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t bodies,capacity,used=0,i; + if(report==NULL)report=&ignored; + if(roots==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(region==NULL||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iarena ); + table_arena_init_with_allocator( &builder->arena, allocator ); builder->main = table_worker_make( &builder->arena ); builder->root_ref.value = 0; builder->region = NULL; @@ -4797,10 +12191,12 @@ static SCHEMA_UNUSED int album_builder_init( AlbumBuilder * builder ) return 1; } +static SCHEMA_UNUSED int album_builder_init(AlbumBuilder * builder) { return album_builder_init_with_allocator(builder,table_default_allocator()); } + static SCHEMA_UNUSED void album_builder_shutdown( AlbumBuilder * builder ) { table_arena_shutdown( &builder->arena ); - free( builder->region ); + table_release(builder->arena.allocator,builder->region); builder->region = NULL; builder->region_bytes = 0; } @@ -4812,119 +12208,2129 @@ static SCHEMA_UNUSED Album * album_builder_root( AlbumBuilder * builder ) return (Album *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); } -/* Lock is ONE WAY and it is the compaction: the segmented arena becomes one - exact-packed region with zero slack, references rewritten self-relative, - and the mutable life released. Single-threaded: call it after the workers - have joined. The region comes back in builder->region. */ -static SCHEMA_UNUSED int album_builder_lock( AlbumBuilder * builder ) -{ - TableCtx ctx; - const Album * root; - int64_t below, total, used; - uint8_t * packed; - if ( builder->arena.locked ) { return builder->region != NULL; } - if ( builder->root_ref.value == 0 ) { return 0; } - ctx.arena = &builder->arena; - root = (const Album *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); - below = album_pack_measure( &ctx, root, 1 ); - if ( below < 0 ) { return 0; } /* a data cycle, or a chain past kTableMaxDepth */ - total = table_align_up64( (int64_t) sizeof( Album ) ) + below; - packed = (uint8_t *) malloc( (size_t) total ); /* the AUTHORING path may allocate */ - if ( packed == NULL ) { return 0; } - memset( packed, 0, (size_t) total ); - used = table_align_up64( (int64_t) sizeof( Album ) ); - if ( !album_pack( &ctx, root, (Album *) (void *) packed, packed, total, &used, 1 ) || used != total ) - { - free( packed ); - return 0; - } - builder->region = packed; - builder->region_bytes = total; - builder->arena.locked = 1; /* MONOTONIC: there is no unlock */ - table_arena_shutdown( &builder->arena ); - return 1; +static SCHEMA_UNUSED int album_builder_lock(AlbumBuilder * builder) +{ + TableCtx ctx; const Album * root; uint8_t * packed; int64_t bytes; + if (builder->arena.locked) { return builder->region!=NULL; } + if (builder->root_ref.value==0) { return 0; } + ctx.arena=&builder->arena; root=(const Album *)table_arena_at(&builder->arena,(uint32_t)builder->root_ref.value); + packed=table_graph_pack(&ctx,root,&schema_graphdemo_album_node_type_,&bytes); + if (packed==NULL) { return 0; } + builder->region=packed; builder->region_bytes=bytes; builder->arena.locked=1; + table_arena_shutdown(&builder->arena); return 1; } -/* ---- Album on the wire: the generic, tolerant form (docs/SPEC-TABLES.md §3) ---- - - The CONTEXT says which form the root is in: NULL, or a ctx with a NULL - arena, is a packed REGION — what Lock and Load produce — and a ctx naming - a builder's arena is the mutable form. C++ spells the two as overloads; - one parameter says the same thing here. */ -static SCHEMA_UNUSED int64_t album_measure( const TableCtx * ctx, const Album * root ) +static SCHEMA_UNUSED const TableNodeType * schema_graphdemo_album_node_lookup_(uint64_t id) { - return root != NULL ? album_measure_body( ctx, root, 1 ) : -1; + switch (id) { + case UINT64_C(0x69ff34904242a73d): return &schema_graphdemo_tally_node_type_; + case UINT64_C(0xd6458a3eef83d457): return &schema_graphdemo_marker_node_type_; + case UINT64_C(0xf60ec899a5a69fa9): return &schema_graphdemo_list_node_node_type_; + default: return NULL; + } } - -static SCHEMA_UNUSED int64_t album_save( const TableCtx * ctx, const Album * root, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int64_t album_measure_with_allocator(const TableCtx * ctx, const Album * root, TableAllocator allocator) { - TableWriter w; - if ( root == NULL ) { return -1; } - w = table_writer_make( buffer, capacity ); - if ( !album_save_body( ctx, &w, root, 1 ) ) { return -1; } - return w.offset; /* == album_measure( ctx, root ) */ + return table_graph_save(ctx,root,&schema_graphdemo_album_node_type_,NULL,INT64_MAX,allocator); } - -/* album_load_measure: the exact region bytes a wire buffer will need. The - caller owns the allocation — generated load code allocates nothing. */ -static SCHEMA_UNUSED int64_t album_load_measure( const uint8_t * wire, int64_t wire_bytes ) +static SCHEMA_UNUSED int64_t album_measure(const TableCtx * ctx,const Album * root) +{ return album_measure_with_allocator(ctx,root,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t album_save_with_allocator(const TableCtx * ctx, const Album * root, uint8_t * buffer, int64_t capacity, TableAllocator allocator) { - TableReport ignored; - TableReader r; - int64_t below; - memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( wire, wire_bytes, &ignored ); - below = album_load_measure_body( &r, 1 ); - if ( below < 0 ) { below = 0; } - return table_align_up64( (int64_t) sizeof( Album ) ) + below; -} - -/* album_load: decode the tolerant wire into the caller's exact-sized region and - return the root. Partial results are kept, as everywhere on this wire — - the report says what happened. NULL means the CALLER's buffer was wrong. */ -static SCHEMA_UNUSED const Album * album_load( uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - TableRegionSink region_sink; - TableSink sink; - Album * root; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - if ( region == NULL || region_bytes < (int64_t) sizeof( Album ) ) { out->malformed = 1; return NULL; } - if ( ( ( (uintptr_t) region ) & ( kTableAlign - 1 ) ) != 0 ) { out->malformed = 1; return NULL; } - memset( region, 0, (size_t) region_bytes ); - region_sink.base = region; - region_sink.capacity = region_bytes; - region_sink.used = table_align_up64( (int64_t) sizeof( Album ) ); - sink.region = ®ion_sink; - sink.worker = NULL; - root = (Album *) (void *) region; - r = table_reader_make( wire, wire_bytes, out ); - album_load_body( &r, &sink, root, 1 ); - return root; -} - -/* album_load_builder: the TOOL's path — the same tolerant decode into a fresh - builder, so loaded data can be edited and locked again. */ -static SCHEMA_UNUSED int album_load_builder( AlbumBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - Album * root; - TableSink sink; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - root = album_builder_root( builder ); - if ( root == NULL ) { out->malformed = 1; return 0; } - sink.region = NULL; - sink.worker = &builder->main; - r = table_reader_make( wire, wire_bytes, out ); - return album_load_body( &r, &sink, root, 1 ); + if (buffer==NULL || capacity<0) { return -1; } + return table_graph_save(ctx,root,&schema_graphdemo_album_node_type_,buffer,capacity,allocator); } - +static SCHEMA_UNUSED int64_t album_save(const TableCtx * ctx,const Album * root, uint8_t * buffer, int64_t capacity) +{ return album_save_with_allocator(ctx,root,buffer,capacity,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t schema_graphdemo_album_load_layout_(const TableReader * reader, int64_t * data_out, int64_t * records_out) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id; + int64_t data=schema_graphdemo_album_wire_storage_(reader), records=0; + if(data<0) { return -1; } + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_album_node_lookup_(id); + if (type!=NULL) { int64_t size=table_node_wire_storage(type,&body); if (size<0 || size>INT64_MAX-data) { return -1; } data+=size; } + records++; + } + if (records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1 || (records+1)*(int64_t)sizeof(TableNodeDirEntry)>INT64_MAX-data) { return -1; } + *data_out=data; *records_out=records; return data+(records+1)*(int64_t)sizeof(TableNodeDirEntry); +} +static SCHEMA_UNUSED int64_t album_load_measure_ex(const uint8_t * wire, int64_t bytes, int64_t * attribution, TableRefuseReason * reason) +{ + TableReport report; TableReader reader; int64_t data,records,total; memset(&report,0,sizeof(report)); + if (attribution!=NULL) { *attribution=0; } + if (!table_wire_open(&reader,wire,bytes,&report)) { if(report.refused && reason!=NULL) { *reason=SCHEMA_TABLE_REFUSE_UNKNOWN_FORM; } return -1; } + report.reason=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH; total=schema_graphdemo_album_load_layout_(&reader,&data,&records); + if(total<0 && reason!=NULL) { *reason=report.reason; } + if(total>=0 && attribution!=NULL) { *attribution=(records+1)*(int64_t)sizeof(TableNodeDirEntry); } return total; +} +static SCHEMA_UNUSED int64_t album_load_measure(const uint8_t * wire, int64_t bytes) +{ return album_load_measure_ex(wire,bytes,NULL,NULL); } +static SCHEMA_UNUSED int schema_graphdemo_album_load_graph_(TableReader * reader, Album * root, TableNodeMap * nodes, TableNodeDirEntry * directory, TableSink * sink) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id,k=0; int32_t unknown=0; + album_reset(root); + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_album_node_lookup_(id); + directory[k+1].type_id=id; directory[k+1].offset=UINT64_MAX; + if (type!=NULL && type->blob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; k++; continue; } + if (type==NULL) { unknown++; } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; return 0; } + directory[k+1].offset=(uint64_t)at; sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; return 0; } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; } else { reader->report->malformed=1; } + if(nodes->good) { + scan=table_node_scan_begin(reader); k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; TableSink carve; const TableNodeType * type=schema_graphdemo_album_node_lookup_(id); + carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; extent.used=type->size; extent.capacity=table_node_wire_storage(type,&body); carve.region=&extent; } + nodes->sink=&carve; body.nodes=nodes; + switch(id) { + case UINT64_C(0x69ff34904242a73d): tally_load_body(&body,(Tally *)value); break; + case UINT64_C(0xd6458a3eef83d457): marker_load_body(&body,(Marker *)value); break; + case UINT64_C(0xf60ec899a5a69fa9): list_node_load_body(&body,(ListNode *)value); break; + default: break; + } + nodes->sink=NULL; if(nodes->refused) { return 0; } + } + k++; + } + } + reader->nodes=nodes; reader->nested=0; + { int read_ok; TableRegionSink extent; TableSink carve; carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; extent.used=sizeof(*root); extent.capacity=schema_graphdemo_album_wire_storage_(reader); carve.region=&extent; } + nodes->sink=&carve; read_ok=album_load_body(reader,root); nodes->sink=NULL; return !nodes->refused && (!nodes->arena || read_ok); } +} +static SCHEMA_UNUSED const Album * album_load(uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableRegionSink place; TableSink sink; int64_t data,records,total; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return NULL; } + total=schema_graphdemo_album_load_layout_(&reader,&data,&records); + if(total<0 || region==NULL || region_bytesmalformed=1; return NULL; } + memset(region,0,(size_t)total); directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; directory[0].type_id=UINT64_C(0xd858c2cb7f1514cc); + nodes.base=region; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=0; nodes.refused=0; nodes.sink=NULL; + place.base=region; place.capacity=data; place.used=schema_graphdemo_album_wire_storage_(&reader); sink.region=&place; sink.worker=NULL; + if(!schema_graphdemo_album_load_graph_(&reader,(Album *)(void *)region,&nodes,directory,&sink)) { return NULL; } + return (const Album *)(const void *)region; +} +static SCHEMA_UNUSED int album_load_builder(AlbumBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableSink sink; TableNodeScan scan; TableReader body; uint64_t id; int64_t records=0; Album * root; int ok; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return 0; } + scan=table_node_scan_begin(&reader); while(table_node_scan_next(&scan,&id,&body)) { records++; } + if(records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1) { report->malformed=1; return 0; } + root=album_builder_root(builder); if(root==NULL) { report->malformed=1; return 0; } + directory=(TableNodeDirEntry *)table_allocate(builder->arena.allocator,(records+1)*(int64_t)sizeof(TableNodeDirEntry)); + if(directory==NULL) { report->malformed=1; return 0; } + directory[0].offset=(uint64_t)builder->root_ref.value; directory[0].type_id=UINT64_C(0xd858c2cb7f1514cc); + nodes.base=NULL; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=1; nodes.refused=0; nodes.sink=NULL; sink.region=NULL; sink.worker=&builder->main; + ok=schema_graphdemo_album_load_graph_(&reader,root,&nodes,directory,&sink); table_release(builder->arena.allocator,directory); return ok; +} +static SCHEMA_UNUSED int schema_graphdemo_album_message_graph_save_(TableBitWriter * w,const Album * root,TableAllocator allocator) +{ + TableNumbering numbering; + uint64_t i; + int ok=0; + if(!table_number_build(&numbering,NULL,root,&schema_graphdemo_album_node_type_,allocator))goto done; + w->nodes=&numbering; + w->index_bits=table_bits_required(0,(int64_t)numbering.count); + if(numbering.count-1>UINT32_MAX)goto done; + if(numbering.count>1){ + table_bit_put(w,37,kTableMessageRefBitsHere); + table_bit_put(w,numbering.count-1,32); + for(i=1;itype->id){ + case UINT64_C(0x69ff34904242a73d): + table_bit_put(w,50,kTableMessageRefBitsHere); + if(!tally_save_message_body(w,(const Tally *)node->value))goto done; + break; + case UINT64_C(0xd6458a3eef83d457): + table_bit_put(w,45,kTableMessageRefBitsHere); + if(!marker_save_message_body(w,(const Marker *)node->value))goto done; + break; + case UINT64_C(0xf60ec899a5a69fa9): + table_bit_put(w,44,kTableMessageRefBitsHere); + if(!list_node_save_message_body(w,(const ListNode *)node->value))goto done; + break; + default:goto done; + } } }ok=album_save_message_body(w,root); + done:table_number_dispose(&numbering); + w->nodes=NULL; + return ok&&!w->overflow; +} +static SCHEMA_UNUSED int64_t album_measure_messages_with_allocator(const Album * const * roots,int64_t count,TableReport * report,TableAllocator allocator) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(roots==NULL||count<1)return -1; + if(count>kTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL||capacity<0)return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid; + if(*id==UINT64_C(0x2f2ec0474f1c4fe4)||*id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_message_blob_scan(r,length))return 0; + return *size>=0; + }switch(*id){ + case UINT64_C(0x69ff34904242a73d):at=sizeof(Tally); + if(!schema_graphdemo_tally_message_extent_(r,&at))return 0; + break; + case UINT64_C(0xd6458a3eef83d457):at=sizeof(Marker); + if(!schema_graphdemo_marker_message_extent_(r,&at))return 0; + break; + case UINT64_C(0xf60ec899a5a69fa9):at=sizeof(ListNode); + if(!schema_graphdemo_list_node_message_extent_(r,&at))return 0; + break; + default:return table_message_skip_body(r); + }if(at>INT64_MAX-kTableAlign)return 0; + *size=table_align_up64(at); + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_album_message_storage_(TableMessageReader * r,uint64_t * records,int64_t * data,int64_t * root_bytes,int * complete) +{ + uint64_t i,id,length; + int64_t size,root=sizeof(Album); + *data=0; + *complete=1; + if(!table_message_nodes_open(r,records))return 0; + for(i=0;i<*records;i++){ + if(!schema_graphdemo_album_message_record_scan_(r,&id,&size,&length)||size>INT64_MAX-*data)return 0; + *data+=size; + } + if(!schema_graphdemo_album_message_extent_(r,&root))*complete=0; + if(r->extent_refused)return 0; + if(root>INT64_MAX-kTableAlign)return 0; + *root_bytes=table_align_up64(root); + if(*root_bytes>INT64_MAX-*data)return 0; + *data+=*root_bytes; + return 1; +} +static SCHEMA_UNUSED int64_t album_load_measure_messages_ex(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,int64_t * attribution,TableRefuseReason * reason) +{ + TableReport report={0}; + TableMessageReader r; + int64_t total=0,dirs=0,bodies,i; + if(attribution)*attribution=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,&report); + if(bodies<0)return -1; + for(i=0;i=(uint64_t)(INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)))return -1; + dir=(int64_t)(records+1)*(int64_t)sizeof(TableNodeDirEntry); + if(data>INT64_MAX-dir || data+dir>INT64_MAX-total)return -1; + total+=data+dir; + dirs+=dir; + if(!complete)break; + }if(attribution)*attribution=dirs; + return total; +} +static SCHEMA_UNUSED int64_t album_load_measure_messages(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes) +{ return album_load_measure_messages_ex(vocabulary,buffer,bytes,NULL,NULL); +} +static SCHEMA_UNUSED int schema_graphdemo_album_message_load_into_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Album ** out) +{ + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Album); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Album * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Album *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0xd858c2cb7f1514cc); + *used+=root_size; + album_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0x69ff34904242a73d):carve.used=sizeof(Tally); + carve.capacity=sizeof(Tally); + scan=*r; + if(!schema_graphdemo_tally_message_extent_(&scan,&carve.capacity))goto malformed; + if(!tally_load_message_body(r,(Tally *)(void *)value))goto malformed; + break; + case UINT64_C(0xd6458a3eef83d457):carve.used=sizeof(Marker); + carve.capacity=sizeof(Marker); + scan=*r; + if(!schema_graphdemo_marker_message_extent_(&scan,&carve.capacity))goto malformed; + if(!marker_load_message_body(r,(Marker *)(void *)value))goto malformed; + break; + case UINT64_C(0xf60ec899a5a69fa9):carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body(r,(ListNode *)(void *)value))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=album_load_message_body(r,root); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int album_load_messages(const Album ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t bodies,capacity,used=0,i; + if(report==NULL)report=&ignored; + if(roots==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(region==NULL||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iblob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return 0; + } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; + k++; + continue; + } + if (type==NULL) { unknown++; + } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=(uint64_t)at; + sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; + if(retention.retain)reader->report->retain_lost+=unknown; + } else { reader->report->malformed=1; + } + if(nodes->good) { + scan=table_node_scan_begin(reader); + k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; + TableSink carve; + const TableNodeType * type=schema_graphdemo_list_node_node_lookup_(id); + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; + extent.used=type->size; + extent.capacity=table_node_wire_storage(type,&body); + carve.region=&extent; + } + nodes->sink=&carve; + body.nodes=nodes; + switch(id) { + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)k+2); + list_node_load_body_retain(&body,(ListNode *)value, retention); + break; + default: break; + } + nodes->sink=NULL; + if(nodes->refused) { return 0; + } + } + k++; + } + } + reader->nodes=nodes; + reader->nested=0; + { int read_ok; + TableRegionSink extent; + TableSink carve; + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; + extent.used=sizeof(*root); + extent.capacity=schema_graphdemo_list_node_wire_storage_(reader); + carve.region=&extent; + } + nodes->sink=&carve; + retention.path=table_retain_root(root,1); + read_ok=list_node_load_body_retain(reader,root, retention); + nodes->sink=NULL; + return !nodes->refused && (!nodes->arena || read_ok); + } +} +static SCHEMA_UNUSED const ListNode * list_node_load_retain(uint8_t * region,int64_t region_bytes,const uint8_t * wire,int64_t wire_bytes,TableRetain * retain,TableReport * report) +{ + TableReport ignored={0}; + TableReader reader; + TableNodeMap nodes; + TableNodeDirEntry * directory; + TableRegionSink place; + TableSink sink; + TableRetainWalk retention; + int64_t data,records,total; + if(report==NULL)report=&ignored; + table_retain_reset(retain,NULL,NULL); + if(!table_wire_open(&reader,wire,wire_bytes,report))return NULL; + total=schema_graphdemo_list_node_load_layout_(&reader,&data,&records); + if(total<0||region==NULL||region_bytesmalformed=1; + return NULL; + } + memset(region,0,(size_t)total); + directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; + directory[0].type_id=UINT64_C(0xf60ec899a5a69fa9); + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=(uint64_t)records+1; + place.base=region; + place.capacity=data; + place.used=schema_graphdemo_list_node_wire_storage_(&reader); + sink.region=&place; + sink.worker=NULL; + table_retain_reset(retain,&nodes,region); + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.path=table_retain_root(region,1); + if(!schema_graphdemo_list_node_load_graph_retain_(&reader,(ListNode *)(void *)region,&nodes,directory,&sink,retention))return NULL; + return (const ListNode *)(const void *)region; +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_retain_save_node_(TableWriter * w,const void * value,uint64_t id,TableRetainWalk retention) +{ + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):return list_node_save_body_retain(w,(const ListNode *)value,retention); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t list_node_measure_retain_with_allocator(const ListNode * root,TableRetain * retain,TableAllocator allocator) +{if(!root)return -1; +return table_retain_graph_save(root,&schema_graphdemo_list_node_node_type_,retain,NULL,INT64_MAX,NULL,allocator,schema_graphdemo_list_node_retain_save_node_); +} +static SCHEMA_UNUSED int64_t list_node_measure_retain(const ListNode * root,TableRetain * retain) +{return list_node_measure_retain_with_allocator(root,retain,table_default_allocator()); +} +static SCHEMA_UNUSED int64_t list_node_save_retain_with_allocator(const ListNode * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator) +{if(!root||!buffer||capacity<0||!report)return -1; +return table_retain_graph_save(root,&schema_graphdemo_list_node_node_type_,retain,buffer,capacity,report,allocator,schema_graphdemo_list_node_retain_save_node_); +} +static SCHEMA_UNUSED int64_t list_node_save_retain(const ListNode * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report) +{return list_node_save_retain_with_allocator(root,retain,buffer,capacity,report,table_default_allocator()); +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_message_load_into_retain_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const ListNode ** out, TableRetainWalk retention) +{ + (void)retention; + + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(ListNode); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + ListNode * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + r->report->retain_lost += retention.retain != NULL; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(ListNode *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0xf60ec899a5a69fa9); + *used+=root_size; + list_node_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + table_retain_reset(retention.retain,&nodes,region); + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0xf60ec899a5a69fa9 */carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body_retain(r,(ListNode *)(void *)value, retention))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + retention.path=table_retain_root(root,1); + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=list_node_load_message_body_retain(r,root, retention); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int list_node_load_retain_messages(const ListNode ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableRetain * retains,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + TableRetainWalk retention; + int64_t bodies,capacity,used=0,i; + if(!report)report=&ignored; + if(!roots||!count){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(!region||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iblob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return 0; + } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; + k++; + continue; + } + if (type==NULL) { unknown++; + } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=(uint64_t)at; + sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; + if(retention.retain)reader->report->retain_lost+=unknown; + } else { reader->report->malformed=1; + } + if(nodes->good) { + scan=table_node_scan_begin(reader); + k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; + TableSink carve; + const TableNodeType * type=schema_graphdemo_tree_node_node_lookup_(id); + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; + extent.used=type->size; + extent.capacity=table_node_wire_storage(type,&body); + carve.region=&extent; + } + nodes->sink=&carve; + body.nodes=nodes; + switch(id) { + case UINT64_C(0xb97e90a3784c431d):retention.path=table_retain_root(value,(uint32_t)k+2); + tree_node_load_body_retain(&body,(TreeNode *)value, retention); + break; + default: break; + } + nodes->sink=NULL; + if(nodes->refused) { return 0; + } + } + k++; + } + } + reader->nodes=nodes; + reader->nested=0; + { int read_ok; + TableRegionSink extent; + TableSink carve; + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; + extent.used=sizeof(*root); + extent.capacity=schema_graphdemo_tree_node_wire_storage_(reader); + carve.region=&extent; + } + nodes->sink=&carve; + retention.path=table_retain_root(root,1); + read_ok=tree_node_load_body_retain(reader,root, retention); + nodes->sink=NULL; + return !nodes->refused && (!nodes->arena || read_ok); + } +} +static SCHEMA_UNUSED const TreeNode * tree_node_load_retain(uint8_t * region,int64_t region_bytes,const uint8_t * wire,int64_t wire_bytes,TableRetain * retain,TableReport * report) +{ + TableReport ignored={0}; + TableReader reader; + TableNodeMap nodes; + TableNodeDirEntry * directory; + TableRegionSink place; + TableSink sink; + TableRetainWalk retention; + int64_t data,records,total; + if(report==NULL)report=&ignored; + table_retain_reset(retain,NULL,NULL); + if(!table_wire_open(&reader,wire,wire_bytes,report))return NULL; + total=schema_graphdemo_tree_node_load_layout_(&reader,&data,&records); + if(total<0||region==NULL||region_bytesmalformed=1; + return NULL; + } + memset(region,0,(size_t)total); + directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; + directory[0].type_id=UINT64_C(0xb97e90a3784c431d); + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=(uint64_t)records+1; + place.base=region; + place.capacity=data; + place.used=schema_graphdemo_tree_node_wire_storage_(&reader); + sink.region=&place; + sink.worker=NULL; + table_retain_reset(retain,&nodes,region); + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.path=table_retain_root(region,1); + if(!schema_graphdemo_tree_node_load_graph_retain_(&reader,(TreeNode *)(void *)region,&nodes,directory,&sink,retention))return NULL; + return (const TreeNode *)(const void *)region; +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_retain_save_node_(TableWriter * w,const void * value,uint64_t id,TableRetainWalk retention) +{ + switch(id){ + case UINT64_C(0xb97e90a3784c431d):return tree_node_save_body_retain(w,(const TreeNode *)value,retention); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t tree_node_measure_retain_with_allocator(const TreeNode * root,TableRetain * retain,TableAllocator allocator) +{if(!root)return -1; +return table_retain_graph_save(root,&schema_graphdemo_tree_node_node_type_,retain,NULL,INT64_MAX,NULL,allocator,schema_graphdemo_tree_node_retain_save_node_); +} +static SCHEMA_UNUSED int64_t tree_node_measure_retain(const TreeNode * root,TableRetain * retain) +{return tree_node_measure_retain_with_allocator(root,retain,table_default_allocator()); +} +static SCHEMA_UNUSED int64_t tree_node_save_retain_with_allocator(const TreeNode * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator) +{if(!root||!buffer||capacity<0||!report)return -1; +return table_retain_graph_save(root,&schema_graphdemo_tree_node_node_type_,retain,buffer,capacity,report,allocator,schema_graphdemo_tree_node_retain_save_node_); +} +static SCHEMA_UNUSED int64_t tree_node_save_retain(const TreeNode * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report) +{return tree_node_save_retain_with_allocator(root,retain,buffer,capacity,report,table_default_allocator()); +} +static SCHEMA_UNUSED int schema_graphdemo_tree_node_message_load_into_retain_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const TreeNode ** out, TableRetainWalk retention) +{ + (void)retention; + + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(TreeNode); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + TreeNode * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + r->report->retain_lost += retention.retain != NULL; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(TreeNode *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0xb97e90a3784c431d); + *used+=root_size; + tree_node_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + table_retain_reset(retention.retain,&nodes,region); + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xb97e90a3784c431d):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0xb97e90a3784c431d */carve.used=sizeof(TreeNode); + carve.capacity=sizeof(TreeNode); + scan=*r; + if(!schema_graphdemo_tree_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!tree_node_load_message_body_retain(r,(TreeNode *)(void *)value, retention))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + retention.path=table_retain_root(root,1); + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=tree_node_load_message_body_retain(r,root, retention); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int tree_node_load_retain_messages(const TreeNode ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableRetain * retains,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + TableRetainWalk retention; + int64_t bodies,capacity,used=0,i; + if(!report)report=&ignored; + if(!roots||!count){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(!region||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iblob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return 0; + } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; + k++; + continue; + } + if (type==NULL) { unknown++; + } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=(uint64_t)at; + sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; + if(retention.retain)reader->report->retain_lost+=unknown; + } else { reader->report->malformed=1; + } + if(nodes->good) { + scan=table_node_scan_begin(reader); + k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; + TableSink carve; + const TableNodeType * type=schema_graphdemo_layer_node_lookup_(id); + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; + extent.used=type->size; + extent.capacity=table_node_wire_storage(type,&body); + carve.region=&extent; + } + nodes->sink=&carve; + body.nodes=nodes; + switch(id) { + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)k+2); + list_node_load_body_retain(&body,(ListNode *)value, retention); + break; + default: break; + } + nodes->sink=NULL; + if(nodes->refused) { return 0; + } + } + k++; + } + } + reader->nodes=nodes; + reader->nested=0; + { int read_ok; + TableRegionSink extent; + TableSink carve; + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; + extent.used=sizeof(*root); + extent.capacity=schema_graphdemo_layer_wire_storage_(reader); + carve.region=&extent; + } + nodes->sink=&carve; + retention.path=table_retain_root(root,1); + read_ok=layer_load_body_retain(reader,root, retention); + nodes->sink=NULL; + return !nodes->refused && (!nodes->arena || read_ok); + } +} +static SCHEMA_UNUSED const Layer * layer_load_retain(uint8_t * region,int64_t region_bytes,const uint8_t * wire,int64_t wire_bytes,TableRetain * retain,TableReport * report) +{ + TableReport ignored={0}; + TableReader reader; + TableNodeMap nodes; + TableNodeDirEntry * directory; + TableRegionSink place; + TableSink sink; + TableRetainWalk retention; + int64_t data,records,total; + if(report==NULL)report=&ignored; + table_retain_reset(retain,NULL,NULL); + if(!table_wire_open(&reader,wire,wire_bytes,report))return NULL; + total=schema_graphdemo_layer_load_layout_(&reader,&data,&records); + if(total<0||region==NULL||region_bytesmalformed=1; + return NULL; + } + memset(region,0,(size_t)total); + directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; + directory[0].type_id=UINT64_C(0x9d167ef77aed79b6); + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=(uint64_t)records+1; + place.base=region; + place.capacity=data; + place.used=schema_graphdemo_layer_wire_storage_(&reader); + sink.region=&place; + sink.worker=NULL; + table_retain_reset(retain,&nodes,region); + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.path=table_retain_root(region,1); + if(!schema_graphdemo_layer_load_graph_retain_(&reader,(Layer *)(void *)region,&nodes,directory,&sink,retention))return NULL; + return (const Layer *)(const void *)region; +} +static SCHEMA_UNUSED int schema_graphdemo_layer_retain_save_node_(TableWriter * w,const void * value,uint64_t id,TableRetainWalk retention) +{ + switch(id){ + case UINT64_C(0x9d167ef77aed79b6):return layer_save_body_retain(w,(const Layer *)value,retention); + case UINT64_C(0xf60ec899a5a69fa9):return list_node_save_body_retain(w,(const ListNode *)value,retention); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t layer_measure_retain_with_allocator(const Layer * root,TableRetain * retain,TableAllocator allocator) +{if(!root)return -1; +return table_retain_graph_save(root,&schema_graphdemo_layer_node_type_,retain,NULL,INT64_MAX,NULL,allocator,schema_graphdemo_layer_retain_save_node_); +} +static SCHEMA_UNUSED int64_t layer_measure_retain(const Layer * root,TableRetain * retain) +{return layer_measure_retain_with_allocator(root,retain,table_default_allocator()); +} +static SCHEMA_UNUSED int64_t layer_save_retain_with_allocator(const Layer * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator) +{if(!root||!buffer||capacity<0||!report)return -1; +return table_retain_graph_save(root,&schema_graphdemo_layer_node_type_,retain,buffer,capacity,report,allocator,schema_graphdemo_layer_retain_save_node_); +} +static SCHEMA_UNUSED int64_t layer_save_retain(const Layer * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report) +{return layer_save_retain_with_allocator(root,retain,buffer,capacity,report,table_default_allocator()); +} +static SCHEMA_UNUSED int schema_graphdemo_layer_message_load_into_retain_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Layer ** out, TableRetainWalk retention) +{ + (void)retention; + + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Layer); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Layer * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + r->report->retain_lost += retention.retain != NULL; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Layer *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0x9d167ef77aed79b6); + *used+=root_size; + layer_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + table_retain_reset(retention.retain,&nodes,region); + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0xf60ec899a5a69fa9 */carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body_retain(r,(ListNode *)(void *)value, retention))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + retention.path=table_retain_root(root,1); + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=layer_load_message_body_retain(r,root, retention); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int layer_load_retain_messages(const Layer ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableRetain * retains,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + TableRetainWalk retention; + int64_t bodies,capacity,used=0,i; + if(!report)report=&ignored; + if(!roots||!count){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(!region||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iblob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return 0; + } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; + k++; + continue; + } + if (type==NULL) { unknown++; + } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=(uint64_t)at; + sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; + if(retention.retain)reader->report->retain_lost+=unknown; + } else { reader->report->malformed=1; + } + if(nodes->good) { + scan=table_node_scan_begin(reader); + k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; + TableSink carve; + const TableNodeType * type=schema_graphdemo_scene_node_lookup_(id); + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; + extent.used=type->size; + extent.capacity=table_node_wire_storage(type,&body); + carve.region=&extent; + } + nodes->sink=&carve; + body.nodes=nodes; + switch(id) { + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)k+2); + list_node_load_body_retain(&body,(ListNode *)value, retention); + break; + case UINT64_C(0xb97e90a3784c431d):retention.path=table_retain_root(value,(uint32_t)k+2); + tree_node_load_body_retain(&body,(TreeNode *)value, retention); + break; + case UINT64_C(0x9d8b8aa2b404c2c8):retention.path=table_retain_root(value,(uint32_t)k+2); + settings_load_body_retain(&body,(Settings *)value, retention); + break; + default: break; + } + nodes->sink=NULL; + if(nodes->refused) { return 0; + } + } + k++; + } + } + reader->nodes=nodes; + reader->nested=0; + { int read_ok; + TableRegionSink extent; + TableSink carve; + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; + extent.used=sizeof(*root); + extent.capacity=schema_graphdemo_scene_wire_storage_(reader); + carve.region=&extent; + } + nodes->sink=&carve; + retention.path=table_retain_root(root,1); + read_ok=scene_load_body_retain(reader,root, retention); + nodes->sink=NULL; + return !nodes->refused && (!nodes->arena || read_ok); + } +} +static SCHEMA_UNUSED const Scene * scene_load_retain(uint8_t * region,int64_t region_bytes,const uint8_t * wire,int64_t wire_bytes,TableRetain * retain,TableReport * report) +{ + TableReport ignored={0}; + TableReader reader; + TableNodeMap nodes; + TableNodeDirEntry * directory; + TableRegionSink place; + TableSink sink; + TableRetainWalk retention; + int64_t data,records,total; + if(report==NULL)report=&ignored; + table_retain_reset(retain,NULL,NULL); + if(!table_wire_open(&reader,wire,wire_bytes,report))return NULL; + total=schema_graphdemo_scene_load_layout_(&reader,&data,&records); + if(total<0||region==NULL||region_bytesmalformed=1; + return NULL; + } + memset(region,0,(size_t)total); + directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; + directory[0].type_id=UINT64_C(0x4a9a31623ab5f213); + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=(uint64_t)records+1; + place.base=region; + place.capacity=data; + place.used=schema_graphdemo_scene_wire_storage_(&reader); + sink.region=&place; + sink.worker=NULL; + table_retain_reset(retain,&nodes,region); + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.path=table_retain_root(region,1); + if(!schema_graphdemo_scene_load_graph_retain_(&reader,(Scene *)(void *)region,&nodes,directory,&sink,retention))return NULL; + return (const Scene *)(const void *)region; +} +static SCHEMA_UNUSED int schema_graphdemo_scene_retain_save_node_(TableWriter * w,const void * value,uint64_t id,TableRetainWalk retention) +{ + switch(id){ + case UINT64_C(0x4a9a31623ab5f213):return scene_save_body_retain(w,(const Scene *)value,retention); + case UINT64_C(0xf60ec899a5a69fa9):return list_node_save_body_retain(w,(const ListNode *)value,retention); + case UINT64_C(0xb97e90a3784c431d):return tree_node_save_body_retain(w,(const TreeNode *)value,retention); + case UINT64_C(0x9d8b8aa2b404c2c8):return settings_save_body_retain(w,(const Settings *)value,retention); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t scene_measure_retain_with_allocator(const Scene * root,TableRetain * retain,TableAllocator allocator) +{if(!root)return -1; +return table_retain_graph_save(root,&schema_graphdemo_scene_node_type_,retain,NULL,INT64_MAX,NULL,allocator,schema_graphdemo_scene_retain_save_node_); +} +static SCHEMA_UNUSED int64_t scene_measure_retain(const Scene * root,TableRetain * retain) +{return scene_measure_retain_with_allocator(root,retain,table_default_allocator()); +} +static SCHEMA_UNUSED int64_t scene_save_retain_with_allocator(const Scene * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator) +{if(!root||!buffer||capacity<0||!report)return -1; +return table_retain_graph_save(root,&schema_graphdemo_scene_node_type_,retain,buffer,capacity,report,allocator,schema_graphdemo_scene_retain_save_node_); +} +static SCHEMA_UNUSED int64_t scene_save_retain(const Scene * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report) +{return scene_save_retain_with_allocator(root,retain,buffer,capacity,report,table_default_allocator()); +} +static SCHEMA_UNUSED int schema_graphdemo_scene_message_load_into_retain_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Scene ** out, TableRetainWalk retention) +{ + (void)retention; + + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Scene); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Scene * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + r->report->retain_lost += retention.retain != NULL; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Scene *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0x4a9a31623ab5f213); + *used+=root_size; + scene_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + table_retain_reset(retention.retain,&nodes,region); + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0xf60ec899a5a69fa9 */carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body_retain(r,(ListNode *)(void *)value, retention))goto malformed; + break; + case UINT64_C(0xb97e90a3784c431d):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0xb97e90a3784c431d */carve.used=sizeof(TreeNode); + carve.capacity=sizeof(TreeNode); + scan=*r; + if(!schema_graphdemo_tree_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!tree_node_load_message_body_retain(r,(TreeNode *)(void *)value, retention))goto malformed; + break; + case UINT64_C(0x9d8b8aa2b404c2c8):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0x9d8b8aa2b404c2c8 */carve.used=sizeof(Settings); + carve.capacity=sizeof(Settings); + scan=*r; + if(!schema_graphdemo_settings_message_extent_(&scan,&carve.capacity))goto malformed; + if(!settings_load_message_body_retain(r,(Settings *)(void *)value, retention))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + retention.path=table_retain_root(root,1); + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=scene_load_message_body_retain(r,root, retention); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int scene_load_retain_messages(const Scene ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableRetain * retains,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + TableRetainWalk retention; + int64_t bodies,capacity,used=0,i; + if(!report)report=&ignored; + if(!roots||!count){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(!region||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iblob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return 0; + } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; + k++; + continue; + } + if (type==NULL) { unknown++; + } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=(uint64_t)at; + sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; + if(retention.retain)reader->report->retain_lost+=unknown; + } else { reader->report->malformed=1; + } + if(nodes->good) { + scan=table_node_scan_begin(reader); + k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; + TableSink carve; + const TableNodeType * type=schema_graphdemo_depot_node_lookup_(id); + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; + extent.used=type->size; + extent.capacity=table_node_wire_storage(type,&body); + carve.region=&extent; + } + nodes->sink=&carve; + body.nodes=nodes; + switch(id) { + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)k+2); + list_node_load_body_retain(&body,(ListNode *)value, retention); + break; + default: break; + } + nodes->sink=NULL; + if(nodes->refused) { return 0; + } + } + k++; + } + } + reader->nodes=nodes; + reader->nested=0; + { int read_ok; + TableRegionSink extent; + TableSink carve; + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; + extent.used=sizeof(*root); + extent.capacity=schema_graphdemo_depot_wire_storage_(reader); + carve.region=&extent; + } + nodes->sink=&carve; + retention.path=table_retain_root(root,1); + read_ok=depot_load_body_retain(reader,root, retention); + nodes->sink=NULL; + return !nodes->refused && (!nodes->arena || read_ok); + } +} +static SCHEMA_UNUSED const Depot * depot_load_retain(uint8_t * region,int64_t region_bytes,const uint8_t * wire,int64_t wire_bytes,TableRetain * retain,TableReport * report) +{ + TableReport ignored={0}; + TableReader reader; + TableNodeMap nodes; + TableNodeDirEntry * directory; + TableRegionSink place; + TableSink sink; + TableRetainWalk retention; + int64_t data,records,total; + if(report==NULL)report=&ignored; + table_retain_reset(retain,NULL,NULL); + if(!table_wire_open(&reader,wire,wire_bytes,report))return NULL; + total=schema_graphdemo_depot_load_layout_(&reader,&data,&records); + if(total<0||region==NULL||region_bytesmalformed=1; + return NULL; + } + memset(region,0,(size_t)total); + directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; + directory[0].type_id=UINT64_C(0x327fe6dc702553fd); + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=(uint64_t)records+1; + place.base=region; + place.capacity=data; + place.used=schema_graphdemo_depot_wire_storage_(&reader); + sink.region=&place; + sink.worker=NULL; + table_retain_reset(retain,&nodes,region); + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.path=table_retain_root(region,1); + if(!schema_graphdemo_depot_load_graph_retain_(&reader,(Depot *)(void *)region,&nodes,directory,&sink,retention))return NULL; + return (const Depot *)(const void *)region; +} +static SCHEMA_UNUSED int schema_graphdemo_depot_retain_save_node_(TableWriter * w,const void * value,uint64_t id,TableRetainWalk retention) +{ + switch(id){ + case UINT64_C(0x327fe6dc702553fd):return depot_save_body_retain(w,(const Depot *)value,retention); + case UINT64_C(0xf60ec899a5a69fa9):return list_node_save_body_retain(w,(const ListNode *)value,retention); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t depot_measure_retain_with_allocator(const Depot * root,TableRetain * retain,TableAllocator allocator) +{if(!root)return -1; +return table_retain_graph_save(root,&schema_graphdemo_depot_node_type_,retain,NULL,INT64_MAX,NULL,allocator,schema_graphdemo_depot_retain_save_node_); +} +static SCHEMA_UNUSED int64_t depot_measure_retain(const Depot * root,TableRetain * retain) +{return depot_measure_retain_with_allocator(root,retain,table_default_allocator()); +} +static SCHEMA_UNUSED int64_t depot_save_retain_with_allocator(const Depot * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator) +{if(!root||!buffer||capacity<0||!report)return -1; +return table_retain_graph_save(root,&schema_graphdemo_depot_node_type_,retain,buffer,capacity,report,allocator,schema_graphdemo_depot_retain_save_node_); +} +static SCHEMA_UNUSED int64_t depot_save_retain(const Depot * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report) +{return depot_save_retain_with_allocator(root,retain,buffer,capacity,report,table_default_allocator()); +} +static SCHEMA_UNUSED int schema_graphdemo_depot_message_load_into_retain_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Depot ** out, TableRetainWalk retention) +{ + (void)retention; + + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Depot); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Depot * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + r->report->retain_lost += retention.retain != NULL; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Depot *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0x327fe6dc702553fd); + *used+=root_size; + depot_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + table_retain_reset(retention.retain,&nodes,region); + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0xf60ec899a5a69fa9 */carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body_retain(r,(ListNode *)(void *)value, retention))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + retention.path=table_retain_root(root,1); + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=depot_load_message_body_retain(r,root, retention); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int depot_load_retain_messages(const Depot ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableRetain * retains,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + TableRetainWalk retention; + int64_t bodies,capacity,used=0,i; + if(!report)report=&ignored; + if(!roots||!count){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(!region||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iblob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return 0; + } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; + k++; + continue; + } + if (type==NULL) { unknown++; + } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=(uint64_t)at; + sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; + if(retention.retain)reader->report->retain_lost+=unknown; + } else { reader->report->malformed=1; + } + if(nodes->good) { + scan=table_node_scan_begin(reader); + k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; + TableSink carve; + const TableNodeType * type=schema_graphdemo_album_node_lookup_(id); + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; + extent.used=type->size; + extent.capacity=table_node_wire_storage(type,&body); + carve.region=&extent; + } + nodes->sink=&carve; + body.nodes=nodes; + switch(id) { + case UINT64_C(0x69ff34904242a73d):retention.path=table_retain_root(value,(uint32_t)k+2); + tally_load_body_retain(&body,(Tally *)value, retention); + break; + case UINT64_C(0xd6458a3eef83d457):retention.path=table_retain_root(value,(uint32_t)k+2); + marker_load_body_retain(&body,(Marker *)value, retention); + break; + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)k+2); + list_node_load_body_retain(&body,(ListNode *)value, retention); + break; + default: break; + } + nodes->sink=NULL; + if(nodes->refused) { return 0; + } + } + k++; + } + } + reader->nodes=nodes; + reader->nested=0; + { int read_ok; + TableRegionSink extent; + TableSink carve; + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; + extent.used=sizeof(*root); + extent.capacity=schema_graphdemo_album_wire_storage_(reader); + carve.region=&extent; + } + nodes->sink=&carve; + retention.path=table_retain_root(root,1); + read_ok=album_load_body_retain(reader,root, retention); + nodes->sink=NULL; + return !nodes->refused && (!nodes->arena || read_ok); + } +} +static SCHEMA_UNUSED const Album * album_load_retain(uint8_t * region,int64_t region_bytes,const uint8_t * wire,int64_t wire_bytes,TableRetain * retain,TableReport * report) +{ + TableReport ignored={0}; + TableReader reader; + TableNodeMap nodes; + TableNodeDirEntry * directory; + TableRegionSink place; + TableSink sink; + TableRetainWalk retention; + int64_t data,records,total; + if(report==NULL)report=&ignored; + table_retain_reset(retain,NULL,NULL); + if(!table_wire_open(&reader,wire,wire_bytes,report))return NULL; + total=schema_graphdemo_album_load_layout_(&reader,&data,&records); + if(total<0||region==NULL||region_bytesmalformed=1; + return NULL; + } + memset(region,0,(size_t)total); + directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; + directory[0].type_id=UINT64_C(0xd858c2cb7f1514cc); + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=(uint64_t)records+1; + place.base=region; + place.capacity=data; + place.used=schema_graphdemo_album_wire_storage_(&reader); + sink.region=&place; + sink.worker=NULL; + table_retain_reset(retain,&nodes,region); + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.path=table_retain_root(region,1); + if(!schema_graphdemo_album_load_graph_retain_(&reader,(Album *)(void *)region,&nodes,directory,&sink,retention))return NULL; + return (const Album *)(const void *)region; +} +static SCHEMA_UNUSED int schema_graphdemo_album_retain_save_node_(TableWriter * w,const void * value,uint64_t id,TableRetainWalk retention) +{ + switch(id){ + case UINT64_C(0xd858c2cb7f1514cc):return album_save_body_retain(w,(const Album *)value,retention); + case UINT64_C(0x69ff34904242a73d):return tally_save_body_retain(w,(const Tally *)value,retention); + case UINT64_C(0xd6458a3eef83d457):return marker_save_body_retain(w,(const Marker *)value,retention); + case UINT64_C(0xf60ec899a5a69fa9):return list_node_save_body_retain(w,(const ListNode *)value,retention); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t album_measure_retain_with_allocator(const Album * root,TableRetain * retain,TableAllocator allocator) +{if(!root)return -1; +return table_retain_graph_save(root,&schema_graphdemo_album_node_type_,retain,NULL,INT64_MAX,NULL,allocator,schema_graphdemo_album_retain_save_node_); +} +static SCHEMA_UNUSED int64_t album_measure_retain(const Album * root,TableRetain * retain) +{return album_measure_retain_with_allocator(root,retain,table_default_allocator()); +} +static SCHEMA_UNUSED int64_t album_save_retain_with_allocator(const Album * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator) +{if(!root||!buffer||capacity<0||!report)return -1; +return table_retain_graph_save(root,&schema_graphdemo_album_node_type_,retain,buffer,capacity,report,allocator,schema_graphdemo_album_retain_save_node_); +} +static SCHEMA_UNUSED int64_t album_save_retain(const Album * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report) +{return album_save_retain_with_allocator(root,retain,buffer,capacity,report,table_default_allocator()); +} +static SCHEMA_UNUSED int schema_graphdemo_album_message_load_into_retain_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Album ** out, TableRetainWalk retention) +{ + (void)retention; + + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Album); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Album * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + r->report->retain_lost += retention.retain != NULL; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Album *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0xd858c2cb7f1514cc); + *used+=root_size; + album_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + table_retain_reset(retention.retain,&nodes,region); + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0x69ff34904242a73d):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0x69ff34904242a73d */carve.used=sizeof(Tally); + carve.capacity=sizeof(Tally); + scan=*r; + if(!schema_graphdemo_tally_message_extent_(&scan,&carve.capacity))goto malformed; + if(!tally_load_message_body_retain(r,(Tally *)(void *)value, retention))goto malformed; + break; + case UINT64_C(0xd6458a3eef83d457):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0xd6458a3eef83d457 */carve.used=sizeof(Marker); + carve.capacity=sizeof(Marker); + scan=*r; + if(!schema_graphdemo_marker_message_extent_(&scan,&carve.capacity))goto malformed; + if(!marker_load_message_body_retain(r,(Marker *)(void *)value, retention))goto malformed; + break; + case UINT64_C(0xf60ec899a5a69fa9):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0xf60ec899a5a69fa9 */carve.used=sizeof(ListNode); + carve.capacity=sizeof(ListNode); + scan=*r; + if(!schema_graphdemo_list_node_message_extent_(&scan,&carve.capacity))goto malformed; + if(!list_node_load_message_body_retain(r,(ListNode *)(void *)value, retention))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + retention.path=table_retain_root(root,1); + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=album_load_message_body_retain(r,root, retention); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int album_load_retain_messages(const Album ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableRetain * retains,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + TableRetainWalk retention; + int64_t bodies,capacity,used=0,i; + if(!report)report=&ignored; + if(!roots||!count){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(!region||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;ibuild,4,order); + table_cook_bytes(at+4,value->tag,value->tag_length,9); + table_cook_put(at+4+12,(uint32_t)value->tag_length,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t meta_cook_measure(const Meta * value) { (void)value; return 104; } +static SCHEMA_UNUSED int meta_cook(const Meta * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<104) { return 0; } + memset(raw,0,104); + if(!schema_graphdemo_meta_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+96,UINT64_C(0x6dadeaaee49d6d18),8,order); + table_cook_header(raw,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,24,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_settings_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Settings * value=(const Settings *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->quality,4,order); + table_cook_bytes(at+4,value->label,value->label_length,17); + table_cook_put(at+4+20,(uint32_t)value->label_length,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t settings_cook_measure(const Settings * value) { (void)value; return 112; } +static SCHEMA_UNUSED int settings_cook(const Settings * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<112) { return 0; } + memset(raw,0,112); + if(!schema_graphdemo_settings_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+104,UINT64_C(0x9d8b8aa2b404c2c8),8,order); + table_cook_header(raw,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,32,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_list_node_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const ListNode * value=(const ListNode *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->value,4,order); + table_cook_bytes(at+4,value->name,value->name_length,13); + table_cook_put(at+4+16,(uint32_t)value->name_length,4,order); + if(!table_cook_ref(n,at+24,&value->next,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t list_node_cook_measure_with_allocator(const TableCtx * ctx,const ListNode * value,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_list_node_node_type_,NULL,0,TableByteOrder_Little,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,1); } +static SCHEMA_UNUSED int list_node_cook_with_allocator(const TableCtx * ctx,const ListNode * value,void * output,uint64_t capacity,TableByteOrder order,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_list_node_node_type_,output,capacity,order,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,0)>=0; } +static SCHEMA_UNUSED int64_t list_node_cook_measure(const TableCtx * ctx,const ListNode * value) +{ return list_node_cook_measure_with_allocator(ctx,value,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int list_node_cook(const TableCtx * ctx,const ListNode * value,void * output,uint64_t capacity,TableByteOrder order) +{ return list_node_cook_with_allocator(ctx,value,output,capacity,order,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int schema_graphdemo_tree_node_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const TreeNode * value=(const TreeNode *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->label,value->label_length,13); + table_cook_put(at+0+16,(uint32_t)value->label_length,4,order); + if(!table_cook_ref(n,at+24,&value->left,order)) { return 0; } + if(!table_cook_ref(n,at+32,&value->right,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t tree_node_cook_measure_with_allocator(const TableCtx * ctx,const TreeNode * value,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_tree_node_node_type_,NULL,0,TableByteOrder_Little,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,1); } +static SCHEMA_UNUSED int tree_node_cook_with_allocator(const TableCtx * ctx,const TreeNode * value,void * output,uint64_t capacity,TableByteOrder order,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_tree_node_node_type_,output,capacity,order,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,0)>=0; } +static SCHEMA_UNUSED int64_t tree_node_cook_measure(const TableCtx * ctx,const TreeNode * value) +{ return tree_node_cook_measure_with_allocator(ctx,value,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int tree_node_cook(const TableCtx * ctx,const TreeNode * value,void * output,uint64_t capacity,TableByteOrder order) +{ return tree_node_cook_with_allocator(ctx,value,output,capacity,order,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int schema_graphdemo_layer_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Layer * value=(const Layer *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->depth,4,order); + if(!table_cook_ref(n,at+8,&value->head,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t layer_cook_measure_with_allocator(const TableCtx * ctx,const Layer * value,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_layer_node_type_,NULL,0,TableByteOrder_Little,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,1); } +static SCHEMA_UNUSED int layer_cook_with_allocator(const TableCtx * ctx,const Layer * value,void * output,uint64_t capacity,TableByteOrder order,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_layer_node_type_,output,capacity,order,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,0)>=0; } +static SCHEMA_UNUSED int64_t layer_cook_measure(const TableCtx * ctx,const Layer * value) +{ return layer_cook_measure_with_allocator(ctx,value,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int layer_cook(const TableCtx * ctx,const Layer * value,void * output,uint64_t capacity,TableByteOrder order) +{ return layer_cook_with_allocator(ctx,value,output,capacity,order,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int schema_graphdemo_scene_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Scene * value=(const Scene *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->name,value->name_length,25); + table_cook_put(at+0+28,(uint32_t)value->name_length,4,order); + table_cook_put(at+32,(uint64_t)value->version,4,order); + if(!table_cook_ref(n,at+40,&value->head,order)) { return 0; } + if(!table_cook_ref(n,at+48,&value->tree,order)) { return 0; } + if(!table_cook_ref(n,at+56,&value->settings,order)) { return 0; } + if(!table_cook_ref(n,at+64,&value->alias,order)) { return 0; } + if(!schema_graphdemo_layer_cook_body_(n,at+72,&value->ground,order)) { return 0; } + { int32_t cook_i1; for(cook_i1=0;cook_i1<4;cook_i1++) { + if(!schema_graphdemo_layer_cook_body_(n,at+88+cook_i1*16,&value->layers[cook_i1],order)) { return 0; } + } } + table_cook_put(at+88+64,(uint32_t)value->layers_count,4,order); + if(!schema_graphdemo_meta_cook_body_(n,at+156,&value->meta,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t scene_cook_measure_with_allocator(const TableCtx * ctx,const Scene * value,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_scene_node_type_,NULL,0,TableByteOrder_Little,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,1); } +static SCHEMA_UNUSED int scene_cook_with_allocator(const TableCtx * ctx,const Scene * value,void * output,uint64_t capacity,TableByteOrder order,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_scene_node_type_,output,capacity,order,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,0)>=0; } +static SCHEMA_UNUSED int64_t scene_cook_measure(const TableCtx * ctx,const Scene * value) +{ return scene_cook_measure_with_allocator(ctx,value,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int scene_cook(const TableCtx * ctx,const Scene * value,void * output,uint64_t capacity,TableByteOrder order) +{ return scene_cook_with_allocator(ctx,value,output,capacity,order,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int schema_graphdemo_depot_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Depot * value=(const Depot *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->name,value->name_length,13); + table_cook_put(at+0+16,(uint32_t)value->name_length,4,order); + { int32_t cook_i1; for(cook_i1=0;cook_i1<2;cook_i1++) { + if(!schema_graphdemo_layer_cook_body_(n,at+24+cook_i1*16,&value->banks[cook_i1],order)) { return 0; } + } } + table_cook_put(at+56+20,value->spare_present ? 1 : 0,1,order); + if(!schema_graphdemo_meta_cook_body_(n,at+56,&value->spare,order)) { return 0; } + if(!table_cook_ref(n,at+80,&value->head,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t depot_cook_measure_with_allocator(const TableCtx * ctx,const Depot * value,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_depot_node_type_,NULL,0,TableByteOrder_Little,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,1); } +static SCHEMA_UNUSED int depot_cook_with_allocator(const TableCtx * ctx,const Depot * value,void * output,uint64_t capacity,TableByteOrder order,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_depot_node_type_,output,capacity,order,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,0)>=0; } +static SCHEMA_UNUSED int64_t depot_cook_measure(const TableCtx * ctx,const Depot * value) +{ return depot_cook_measure_with_allocator(ctx,value,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int depot_cook(const TableCtx * ctx,const Depot * value,void * output,uint64_t capacity,TableByteOrder order) +{ return depot_cook_with_allocator(ctx,value,output,capacity,order,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int schema_graphdemo_album_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Album * value=(const Album *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->name,value->name_length,17); + table_cook_put(at+0+20,(uint32_t)value->name_length,4,order); + if(!schema_graphdemo_colour_cook_body_(n,at+24,&value->tint,order)) { return 0; } + if(!schema_graphdemo_stamp_cook_body_(n,at+28,&value->stamp,order)) { return 0; } + if(!schema_graphdemo_marker_cook_body_(n,at+48,&value->marker,order)) { return 0; } + if(!table_cook_ref(n,at+72,&value->pin,order)) { return 0; } + if(!table_cook_ref(n,at+80,&value->head,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t album_cook_measure_with_allocator(const TableCtx * ctx,const Album * value,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_album_node_type_,NULL,0,TableByteOrder_Little,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,1); } +static SCHEMA_UNUSED int album_cook_with_allocator(const TableCtx * ctx,const Album * value,void * output,uint64_t capacity,TableByteOrder order,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_album_node_type_,output,capacity,order,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,0)>=0; } +static SCHEMA_UNUSED int64_t album_cook_measure(const TableCtx * ctx,const Album * value) +{ return album_cook_measure_with_allocator(ctx,value,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int album_cook(const TableCtx * ctx,const Album * value,void * output,uint64_t capacity,TableByteOrder order) +{ return album_cook_with_allocator(ctx,value,output,capacity,order,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared @@ -5250,29 +14832,59 @@ static SCHEMA_UNUSED int64_t settings_to_json( const Settings * value, char * bu return schema_graphdemo_settings_to_json_( value, buffer, capacity ); } -/* ListNode is VARIABLE-LENGTH. Its text form reads through the builder - (docs/SPEC-TABLES.md §16.1), which this backend does not emit yet: - no list_node_from_json and no list_node_to_json exist to call. */ +int schema_graphdemo_list_node_from_json_( ListNodeBuilder * builder, const char * text, int64_t bytes, TableReport * report ); +int64_t schema_graphdemo_list_node_to_json_( const ListNode * value, char * buffer, int64_t capacity, TableAllocator allocator ); +static SCHEMA_UNUSED int list_node_from_json( ListNodeBuilder * builder, const char * text, int64_t bytes, TableReport * report ) { return schema_graphdemo_list_node_from_json_(builder,text,bytes,report); } +static SCHEMA_UNUSED int64_t list_node_to_json_measure( const ListNode * value ) { return schema_graphdemo_list_node_to_json_(value,NULL,0,table_default_allocator()); } +static SCHEMA_UNUSED int64_t list_node_to_json( const ListNode * value, char * buffer, int64_t capacity ) { return schema_graphdemo_list_node_to_json_(value,buffer,capacity,table_default_allocator()); } + +static SCHEMA_UNUSED int64_t list_node_to_json_with_allocator(const ListNode * value,char * buffer,int64_t capacity,TableAllocator allocator) { return schema_graphdemo_list_node_to_json_(value,buffer,capacity,allocator); } +static SCHEMA_UNUSED int64_t list_node_to_json_measure_with_allocator(const ListNode * value,TableAllocator allocator) { return schema_graphdemo_list_node_to_json_(value,NULL,0,allocator); } + +int schema_graphdemo_tree_node_from_json_( TreeNodeBuilder * builder, const char * text, int64_t bytes, TableReport * report ); +int64_t schema_graphdemo_tree_node_to_json_( const TreeNode * value, char * buffer, int64_t capacity, TableAllocator allocator ); +static SCHEMA_UNUSED int tree_node_from_json( TreeNodeBuilder * builder, const char * text, int64_t bytes, TableReport * report ) { return schema_graphdemo_tree_node_from_json_(builder,text,bytes,report); } +static SCHEMA_UNUSED int64_t tree_node_to_json_measure( const TreeNode * value ) { return schema_graphdemo_tree_node_to_json_(value,NULL,0,table_default_allocator()); } +static SCHEMA_UNUSED int64_t tree_node_to_json( const TreeNode * value, char * buffer, int64_t capacity ) { return schema_graphdemo_tree_node_to_json_(value,buffer,capacity,table_default_allocator()); } + +static SCHEMA_UNUSED int64_t tree_node_to_json_with_allocator(const TreeNode * value,char * buffer,int64_t capacity,TableAllocator allocator) { return schema_graphdemo_tree_node_to_json_(value,buffer,capacity,allocator); } +static SCHEMA_UNUSED int64_t tree_node_to_json_measure_with_allocator(const TreeNode * value,TableAllocator allocator) { return schema_graphdemo_tree_node_to_json_(value,NULL,0,allocator); } + +int schema_graphdemo_layer_from_json_( LayerBuilder * builder, const char * text, int64_t bytes, TableReport * report ); +int64_t schema_graphdemo_layer_to_json_( const Layer * value, char * buffer, int64_t capacity, TableAllocator allocator ); +static SCHEMA_UNUSED int layer_from_json( LayerBuilder * builder, const char * text, int64_t bytes, TableReport * report ) { return schema_graphdemo_layer_from_json_(builder,text,bytes,report); } +static SCHEMA_UNUSED int64_t layer_to_json_measure( const Layer * value ) { return schema_graphdemo_layer_to_json_(value,NULL,0,table_default_allocator()); } +static SCHEMA_UNUSED int64_t layer_to_json( const Layer * value, char * buffer, int64_t capacity ) { return schema_graphdemo_layer_to_json_(value,buffer,capacity,table_default_allocator()); } + +static SCHEMA_UNUSED int64_t layer_to_json_with_allocator(const Layer * value,char * buffer,int64_t capacity,TableAllocator allocator) { return schema_graphdemo_layer_to_json_(value,buffer,capacity,allocator); } +static SCHEMA_UNUSED int64_t layer_to_json_measure_with_allocator(const Layer * value,TableAllocator allocator) { return schema_graphdemo_layer_to_json_(value,NULL,0,allocator); } + +int schema_graphdemo_scene_from_json_( SceneBuilder * builder, const char * text, int64_t bytes, TableReport * report ); +int64_t schema_graphdemo_scene_to_json_( const Scene * value, char * buffer, int64_t capacity, TableAllocator allocator ); +static SCHEMA_UNUSED int scene_from_json( SceneBuilder * builder, const char * text, int64_t bytes, TableReport * report ) { return schema_graphdemo_scene_from_json_(builder,text,bytes,report); } +static SCHEMA_UNUSED int64_t scene_to_json_measure( const Scene * value ) { return schema_graphdemo_scene_to_json_(value,NULL,0,table_default_allocator()); } +static SCHEMA_UNUSED int64_t scene_to_json( const Scene * value, char * buffer, int64_t capacity ) { return schema_graphdemo_scene_to_json_(value,buffer,capacity,table_default_allocator()); } -/* TreeNode is VARIABLE-LENGTH. Its text form reads through the builder - (docs/SPEC-TABLES.md §16.1), which this backend does not emit yet: - no tree_node_from_json and no tree_node_to_json exist to call. */ +static SCHEMA_UNUSED int64_t scene_to_json_with_allocator(const Scene * value,char * buffer,int64_t capacity,TableAllocator allocator) { return schema_graphdemo_scene_to_json_(value,buffer,capacity,allocator); } +static SCHEMA_UNUSED int64_t scene_to_json_measure_with_allocator(const Scene * value,TableAllocator allocator) { return schema_graphdemo_scene_to_json_(value,NULL,0,allocator); } -/* Layer is VARIABLE-LENGTH. Its text form reads through the builder - (docs/SPEC-TABLES.md §16.1), which this backend does not emit yet: - no layer_from_json and no layer_to_json exist to call. */ +int schema_graphdemo_depot_from_json_( DepotBuilder * builder, const char * text, int64_t bytes, TableReport * report ); +int64_t schema_graphdemo_depot_to_json_( const Depot * value, char * buffer, int64_t capacity, TableAllocator allocator ); +static SCHEMA_UNUSED int depot_from_json( DepotBuilder * builder, const char * text, int64_t bytes, TableReport * report ) { return schema_graphdemo_depot_from_json_(builder,text,bytes,report); } +static SCHEMA_UNUSED int64_t depot_to_json_measure( const Depot * value ) { return schema_graphdemo_depot_to_json_(value,NULL,0,table_default_allocator()); } +static SCHEMA_UNUSED int64_t depot_to_json( const Depot * value, char * buffer, int64_t capacity ) { return schema_graphdemo_depot_to_json_(value,buffer,capacity,table_default_allocator()); } -/* Scene is VARIABLE-LENGTH. Its text form reads through the builder - (docs/SPEC-TABLES.md §16.1), which this backend does not emit yet: - no scene_from_json and no scene_to_json exist to call. */ +static SCHEMA_UNUSED int64_t depot_to_json_with_allocator(const Depot * value,char * buffer,int64_t capacity,TableAllocator allocator) { return schema_graphdemo_depot_to_json_(value,buffer,capacity,allocator); } +static SCHEMA_UNUSED int64_t depot_to_json_measure_with_allocator(const Depot * value,TableAllocator allocator) { return schema_graphdemo_depot_to_json_(value,NULL,0,allocator); } -/* Depot is VARIABLE-LENGTH. Its text form reads through the builder - (docs/SPEC-TABLES.md §16.1), which this backend does not emit yet: - no depot_from_json and no depot_to_json exist to call. */ +int schema_graphdemo_album_from_json_( AlbumBuilder * builder, const char * text, int64_t bytes, TableReport * report ); +int64_t schema_graphdemo_album_to_json_( const Album * value, char * buffer, int64_t capacity, TableAllocator allocator ); +static SCHEMA_UNUSED int album_from_json( AlbumBuilder * builder, const char * text, int64_t bytes, TableReport * report ) { return schema_graphdemo_album_from_json_(builder,text,bytes,report); } +static SCHEMA_UNUSED int64_t album_to_json_measure( const Album * value ) { return schema_graphdemo_album_to_json_(value,NULL,0,table_default_allocator()); } +static SCHEMA_UNUSED int64_t album_to_json( const Album * value, char * buffer, int64_t capacity ) { return schema_graphdemo_album_to_json_(value,buffer,capacity,table_default_allocator()); } -/* Album is VARIABLE-LENGTH. Its text form reads through the builder - (docs/SPEC-TABLES.md §16.1), which this backend does not emit yet: - no album_from_json and no album_to_json exist to call. */ +static SCHEMA_UNUSED int64_t album_to_json_with_allocator(const Album * value,char * buffer,int64_t capacity,TableAllocator allocator) { return schema_graphdemo_album_to_json_(value,buffer,capacity,allocator); } +static SCHEMA_UNUSED int64_t album_to_json_measure_with_allocator(const Album * value,TableAllocator allocator) { return schema_graphdemo_album_to_json_(value,NULL,0,allocator); } #ifdef __cplusplus diff --git a/testdata/golden/tables/pointers-c/GraphdemoView.c b/testdata/golden/tables/pointers-c/GraphdemoView.c new file mode 100644 index 000000000..7246aee99 --- /dev/null +++ b/testdata/golden/tables/pointers-c/GraphdemoView.c @@ -0,0 +1,41 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#include "GraphdemoView.h" +const UnitViewInfo * schema_graphdemo_unit_view_(void) +{ + static const ViewVariant Tier_view_variants[] = { + { 0, "None", 0, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 1, "Low", 0x24f3a319b88552c1ull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + { 2, "High", 0x9deeefd89ca8a81dull, NULL, NULL, NULL, TableDocNone, 0, NULL }, + }; + static const ViewVocabulary enums[] = { + { "Tier", "Graph.schema", 2, 8, 3, Tier_view_variants, TableDocNone, 0, NULL }, + }; + static const ViewType types[] = { + { "Colour", "Parts.schema", 0, &schema_graphdemo_colour_info_, TableDocNone, 0, NULL }, + }; + static const ViewType tables[] = { + { "Album", "Graph.schema", 1, &schema_graphdemo_album_info_, TableDocNone, 0, NULL }, + { "Depot", "Graph.schema", 1, &schema_graphdemo_depot_info_, TableDocNone, 0, NULL }, + { "Layer", "Graph.schema", 1, &schema_graphdemo_layer_info_, TableDocNone, 0, NULL }, + { "ListNode", "Graph.schema", 1, &schema_graphdemo_list_node_info_, TableDocNone, 0, NULL }, + { "Marker", "Marks.schema", 1, &schema_graphdemo_marker_info_, TableDocNone, 0, NULL }, + { "Meta", "Graph.schema", 1, &schema_graphdemo_meta_info_, TableDocNone, 0, NULL }, + { "Scene", "Graph.schema", 1, &schema_graphdemo_scene_info_, TableDocNone, 0, NULL }, + { "Settings", "Graph.schema", 1, &schema_graphdemo_settings_info_, TableDocNone, 0, NULL }, + { "Stamp", "Parts.schema", 1, &schema_graphdemo_stamp_info_, TableDocNone, 0, NULL }, + { "Tally", "Marks.schema", 1, &schema_graphdemo_tally_info_, TableDocNone, 0, NULL }, + { "TreeNode", "Graph.schema", 1, &schema_graphdemo_tree_node_info_, TableDocNone, 0, NULL }, + }; + static const ViewConstant constants[] = { + { "MaxLayers", "Graph.schema", "int64", 0, INT64_C(4), 0.0, TableDocNone, 0, NULL }, + }; + static const UnitViewInfo info = { "graphdemo", 0x36ff4b4cd88bf37aull, + 1, types, + 11, tables, + 1, enums, + 0, NULL, + 0, NULL, + 1, constants }; + return &info; +} diff --git a/testdata/golden/tables/pointers-c/GraphdemoView.h b/testdata/golden/tables/pointers-c/GraphdemoView.h new file mode 100644 index 000000000..659cc0e25 --- /dev/null +++ b/testdata/golden/tables/pointers-c/GraphdemoView.h @@ -0,0 +1,107 @@ +/* Code generated by the schema compiler. DO NOT EDIT. + SPDX-License-Identifier: NONE — this generated output is yours. */ +#ifndef SCHEMA_GRAPHDEMO_VIEW_H +#define SCHEMA_GRAPHDEMO_VIEW_H +#include +#include +#include +#include "Graph.h" +#include "Marks.h" +#include "Parts.h" +#include "GraphTable.h" +#include "MarksTable.h" +#include "PartsTable.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ViewConstant +{ + const char * name; + const char * file; // the declaring schema file's basename + const char * type_name; // declared storage: "int64", "float64", "int32", ... + int is_float; + int64_t int_value; // the folded value; float_value when is_float + double float_value; + const char * doc; // the `///` block above it, verbatim (SPEC §4.1); + // "" when there is none, never NULL (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; 0 and NULL when there are none +} ViewConstant; + +typedef struct ViewVariant // one enum variant, one flags bit, one union arm +{ + uint64_t value; // an enum's value, a flags BIT INDEX, a union's tag + const char * name; + uint64_t id; // the table-wire id (§5). It is 0 for a flags bit, + // and throughout a vocabulary no table closure + // reaches (§8.2) + const char * payload_name; // a union arm's TYPE as declared — a record's + // name, or a general arm's spelling + // ("int32", "string(64)", "*Chunk"); NULL on + // a payload-free arm and on every other row + const TableTypeInfo * payload; // that payload's descriptor — never NULL for + // an arm naming a `type` or a `table` (§8.2); + // NULL on a general arm, on a payload-free + // arm, on tag 0, and on an enum or flags row + const TableFieldInfo * field; // a general arm's FIELD descriptor: its kind, + // width, bounds, capacity, companions and the + // offsets §8.1 takes within the union's + // storage. NULL on an arm naming a `type` or + // a `table`, on a payload-free arm, on tag 0, + // and on an enum or flags row + const char * doc; // the `///` block above this variant, bit or + // arm, verbatim; "" when there is none (§8.1) + int32_t num_tags; // this row's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none. On a general + // arm the `field` descriptor above carries the + // same two values (§8.1) +} ViewVariant; + +typedef struct ViewVocabulary // an enum, a flags or a union declaration +{ + const char * name; + const char * file; + int64_t max; // highest value / highest bit index / highest tag + int32_t storage_bits; + int32_t num_variants; + const ViewVariant * variants; + const char * doc; // the declaration's own `///` block; "" when none + int32_t num_tags; // the declaration's own tags, in DECLARED order; + const char * const * tags; // 0 and NULL when there are none +} ViewVocabulary; + +typedef struct ViewType // one declaration: a type, or a table +{ + const char * name; + const char * file; + int table; // declared `table` + const TableTypeInfo * type; // §8.1's descriptor: the properties, and this + // declaration's own doc and tags beside them + const char * doc; // the `///` block above it; "" when none. The + // same text `type->doc` carries (§8.1) + int32_t num_tags; // the declaration's tags (SPEC §4.2), in + const char * const * tags; // DECLARED order; the same list `type` carries +} ViewType; + +typedef struct UnitViewInfo +{ + const char * package; + uint64_t protocol_id; + int32_t num_types; const ViewType * types; + int32_t num_tables; const ViewType * tables; + int32_t num_enums; const ViewVocabulary * enums; + int32_t num_flags; const ViewVocabulary * flags; + int32_t num_unions; const ViewVocabulary * unions; + int32_t num_constants; const ViewConstant * constants; +} UnitViewInfo; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + + +const UnitViewInfo * schema_graphdemo_unit_view_(void); +static SCHEMA_UNUSED const UnitViewInfo * unit_view(void) { return schema_graphdemo_unit_view_(); } +#ifdef __cplusplus +} +#endif +#endif diff --git a/testdata/golden/tables/pointers-c/MarksTable.c b/testdata/golden/tables/pointers-c/MarksTable.c index 2fc073ead..76ada4a1a 100644 --- a/testdata/golden/tables/pointers-c/MarksTable.c +++ b/testdata/golden/tables/pointers-c/MarksTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) - { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } - } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1728,15 +2329,461 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ + +/* ---- json graph walk: begin ---- */ +typedef struct TableJsonGraphEntry +{ + uint64_t key; + int64_t count, label; + int open; + uint32_t node; + const TableTypeInfo * type; +} TableJsonGraphEntry; + +typedef struct TableJsonGraphMap +{ + TableJsonGraphEntry * entries; + uint64_t capacity, count; + TableAllocator allocator; +} TableJsonGraphMap; + +static SCHEMA_UNUSED uint64_t table_json_graph_slot( const TableJsonGraphMap * map, uint64_t key ) +{ + uint64_t hash = key * UINT64_C(0x9E3779B97F4A7C15); + uint64_t at; + hash ^= hash >> 29; at = hash & (map->capacity-1); + while ( map->entries[at].key != 0 && map->entries[at].key != key ) { at = (at+1) & (map->capacity-1); } + return at; +} + +static SCHEMA_UNUSED TableJsonGraphEntry * table_json_graph_find( TableJsonGraphMap * map, uint64_t key ) +{ + TableJsonGraphEntry * entry; + if ( map->capacity == 0 ) { return NULL; } + entry = &map->entries[table_json_graph_slot(map,key)]; + return entry->key == key ? entry : NULL; +} + +static SCHEMA_UNUSED TableJsonGraphEntry * table_json_graph_reach( TableJsonGraphMap * map, uint64_t key, int * taken ) +{ + TableJsonGraphEntry * entry; + if ( map->capacity == 0 || map->count >= map->capacity - map->capacity/4 - 1 ) + { + TableJsonGraphMap grown; + uint64_t i; + grown.capacity = map->capacity ? map->capacity*4 : 64; + if ( grown.capacity < map->capacity || grown.capacity > INT64_MAX / sizeof(TableJsonGraphEntry) ) { return NULL; } + grown.count = map->count; grown.allocator=map->allocator; + grown.entries = (TableJsonGraphEntry *)table_allocate(map->allocator,(int64_t)grown.capacity*sizeof(TableJsonGraphEntry)); + if ( grown.entries == NULL ) { return NULL; } + for ( i = 0; i < map->capacity; i++ ) + { + if ( map->entries[i].key ) { grown.entries[table_json_graph_slot(&grown,map->entries[i].key)] = map->entries[i]; } + } + table_release(map->allocator,map->entries); *map = grown; + } + entry = &map->entries[table_json_graph_slot(map,key)]; + *taken = entry->key != key; + if ( *taken ) { entry->key = key; map->count++; } + return entry; +} + +typedef struct TableJsonGraphIn +{ + TableWorker * worker; + TableJsonGraphMap labels; +} TableJsonGraphIn; + +static SCHEMA_UNUSED int table_json_scan_label( TableJsonIn * in, uint64_t * label ) +{ + uint64_t value = 0; + table_json_space(in); + if ( in->pos >= in->size || in->text[in->pos] < '1' || in->text[in->pos] > '9' ) { in->bad = 1; return 0; } + while ( in->pos < in->size && in->text[in->pos] >= '0' && in->text[in->pos] <= '9' ) + { + uint64_t digit = (uint64_t)(in->text[in->pos]-'0'); + if ( value > (UINT64_MAX-digit)/10 ) { in->bad = 1; return 0; } + value = value*10+digit; in->pos++; + } + *label = value; return 1; +} + +static SCHEMA_UNUSED void * table_json_emplace( TableJsonGraphIn * graph, void * slot, const TableTypeInfo * type ) +{ + uint32_t at = table_worker_bump(graph->worker,type->size); + void * node; + if ( at == kTableAllocFailed ) { return NULL; } + node = table_arena_at(graph->worker->arena,at); + type->reset(node); ((TableRef *)slot)->value = at; return node; +} + +static SCHEMA_UNUSED int table_json_read_blob( TableJsonIn * in, void * slot, const TableFieldInfo * f ) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; + TableRef * ref=(TableRef *)slot; + int64_t mark, symbols=0, length, placed=0, at; + int malformed=0, held=0; + uint32_t accumulator=0; + uint8_t * data; + if(graph==NULL || table_json_peek(in)!='"') { in->bad=1; return 0; } + ref->value=0; + if(strcmp(f->type_name,"string")==0) + { + int32_t count=0, written=0; + char * text; + mark=in->pos; + if(!table_json_scan_string(in,NULL,0,&count)) { return 0; } + in->pos=mark; text=table_string_emplace(graph->worker,ref,NULL,count); + if(text==NULL) { in->bad=1; return 0; } + return table_json_scan_string(in,text,count,&written); + } + mark=++in->pos; + for(;;) + { + char c; + if(in->pos>=in->size) { in->bad=1; return 0; } + c=in->text[in->pos++]; + if(c=='"') { break; } + if(c=='=' || malformed) { continue; } + if(table_json_base64_value((uint8_t)c)<0) { malformed=1; continue; } + symbols++; + } + if(malformed) { in->report->kind_mismatch++; return 1; } + length=symbols*6/8; + data=table_bytes_emplace(graph->worker,ref,length); + if(data==NULL) { in->bad=1; return 0; } + for(at=mark; in->text[at]!='"'; at++) + { + int32_t symbol=table_json_base64_value((uint8_t)in->text[at]); + if(symbol<0) { continue; } + accumulator=(accumulator<<6)|(uint32_t)symbol; held+=6; + if(held>=8) { held-=8; if(placed>held)&0xff); } } + } + return 1; +} + +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ + TableJsonGraphIn * graph = (TableJsonGraphIn *)in->graph; + TableJsonGraphEntry * entry; + char key[kTableJsonMaxKey], c; + int32_t length = 0; + uint64_t label = 0; + int taken = 0, bare; + void * node; + if(graph!=NULL && f->table==NULL) { return table_json_read_blob(in,slot,f); } + if ( graph == NULL || depth+1 > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; c = table_json_peek(in); + if ( c == '}' ) + { + in->pos++; + if ( table_json_emplace(graph,slot,f->table) == NULL ) { in->bad = 1; return 0; } + return 1; + } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + if ( strcmp(key,"&node") != 0 ) + { + node = table_json_emplace(graph,slot,f->table); + if ( node == NULL ) { in->bad = 1; return 0; } + return table_json_read_table_keys(in,node,f->table,depth+1,key); + } + if ( !table_json_scan_label(in,&label) ) { return 0; } + entry = table_json_graph_reach(&graph->labels,label,&taken); + if ( entry == NULL ) { in->bad = 1; return 0; } + c = table_json_peek(in); + if ( c == ',' ) { in->pos++; c = table_json_peek(in); } + bare = c == '}'; + if ( bare == taken ) { in->bad = 1; return 0; } + if ( bare ) + { + in->pos++; + if ( entry->open ) { in->bad = 1; return 0; } + ((TableRef *)slot)->value = 0; + if ( entry->type == NULL ) { return 1; } + if ( entry->type != f->table ) { in->report->kind_mismatch++; return 1; } + ((TableRef *)slot)->value = entry->node; return 1; + } + node = table_json_emplace(graph,slot,f->table); + if ( node == NULL ) { in->bad = 1; return 0; } + entry->open = 1; + if ( !table_json_read_table_keys(in,node,f->table,depth+1,NULL) ) { return 0; } + entry = table_json_graph_find(&graph->labels,label); + if ( entry == NULL ) { in->bad = 1; return 0; } + entry->node = (uint32_t)((TableRef *)slot)->value; entry->type = f->table; entry->open = 0; return 1; +} + +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ + TableJsonGraphIn * graph = (TableJsonGraphIn *)in->graph; + uint64_t label = 0; + int taken = 0; + if ( graph == NULL || strcmp(key,"&node") != 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_label(in,&label) ) { return 0; } + if ( table_json_graph_reach(&graph->labels,label,&taken) == NULL ) { in->bad = 1; return 0; } + return 1; +} + +typedef struct TableJsonGraphOut +{ + TableJsonGraphMap nodes; + int counting; + int64_t next_label; +} TableJsonGraphOut; + +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ + TableJsonGraphOut * graph = (TableJsonGraphOut *)out->graph; + const void * node = table_ref_at(NULL,(const TableRef *)slot); + TableJsonGraphEntry * entry; + int taken = 0, any = 1; + int64_t before; + if ( graph == NULL ) { return 0; } + if ( node == NULL ) { table_json_raw(out,"null",4); return 1; } + entry = table_json_graph_reach(&graph->nodes,(uint64_t)(uintptr_t)node,&taken); + if ( entry == NULL ) { return 0; } + if(f->table==NULL) + { + const TableBlob * blob=(const TableBlob *)node; + if(graph->counting) { entry->count++; return 1; } + if(entry->count>1 || blob->length>INT32_MAX) { return 0; } + if(strcmp(f->type_name,"string")==0) { table_json_write_string(out,(const char *)(blob+1),(int32_t)blob->length); } + else { table_json_write_base64(out,(const uint8_t *)(blob+1),(int32_t)blob->length); } + return 1; + } + if ( graph->counting ) + { + entry->count++; + if ( !taken ) { return entry->open == 0; } + entry->open = 1; + if ( !table_json_write_value(out,node,f->table,depth) ) { return 0; } + entry = table_json_graph_find(&graph->nodes,(uint64_t)(uintptr_t)node); + if ( entry == NULL ) { return 0; } + entry->open = 0; return 1; + } + if ( entry->count <= 1 ) { return table_json_write_value(out,node,f->table,depth); } + if ( depth > kTableJsonMaxDepth ) { return 0; } + table_json_put(out,'{'); table_json_line(out,depth+1); table_json_raw(out,"\"&node\": ",9); + if ( entry->label ) + { + table_json_write_unsigned(out,(uint64_t)entry->label); + table_json_line(out,depth); table_json_put(out,'}'); return 1; + } + entry->label = ++graph->next_label; + table_json_write_unsigned(out,(uint64_t)entry->label); + before = out->offset; + if ( !table_json_write_fields(out,node,f->table,depth,&any) || out->offset == before ) { return 0; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + +static SCHEMA_UNUSED int table_json_read_graph( TableWorker * worker, void * root, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) +{ + TableJsonGraphIn graph; + TableJsonIn in; + TableReport ignored = {0}; + int ok; + memset(&graph,0,sizeof(graph)); graph.worker = worker; graph.labels.allocator=worker->arena!=NULL ? worker->arena->allocator : table_default_allocator(); + memset(&in,0,sizeof(in)); in.text = text; in.size = bytes; in.report = report ? report : &ignored; in.graph = &graph; + if ( root == NULL || worker->arena == NULL || text == NULL || bytes < 0 ) { in.report->malformed = 1; return 0; } + info->reset(root); + ok = table_json_read_table(&in,root,info,0); + if ( ok ) { table_json_space(&in); if ( in.pos != in.size ) { in.bad = 1; } } + table_release(graph.labels.allocator,graph.labels.entries); + if ( in.bad || !ok ) { in.report->malformed = 1; return 0; } + return 1; +} + +static SCHEMA_UNUSED int64_t table_json_write_graph( const void * root, const TableTypeInfo * info, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + TableJsonGraphOut graph; + TableJsonOut count, out; + TableJsonGraphEntry * entry; + int taken = 0, ok; + if ( root == NULL ) { return -1; } + memset(&graph,0,sizeof(graph)); graph.counting = 1; graph.nodes.allocator=allocator; + entry = table_json_graph_reach(&graph.nodes,(uint64_t)(uintptr_t)root,&taken); + if ( entry == NULL ) { return -1; } + entry->open = 1; + memset(&count,0,sizeof(count)); count.graph = &graph; + ok = table_json_write_value(&count,root,info,0); + graph.counting = 0; + memset(&out,0,sizeof(out)); out.buffer = buffer; out.capacity = capacity; out.graph = &graph; + if ( ok ) { ok = table_json_write_value(&out,root,info,0); } + table_release(graph.nodes.allocator,graph.nodes.entries); + if ( !ok ) { return -1; } + table_json_put(&out,'\n'); return out.overflow ? -1 : out.offset; +} +/* ---- json graph walk: end ---- */ + +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth) +{ + const TableSequence * list=(const TableSequence *)storage; + TableSequenceCursor cursor=table_sequence_cursor(NULL,list,f->elem_size); int32_t i; + if(!cursor.ok) { return 0; } + table_json_put(out,'['); + for(i=0;icount;i++) { + const void * element=table_sequence_next(&cursor); if(element==NULL) { return 0; } + if(i) { table_json_put(out,','); } table_json_line(out,depth+1); + if(!table_json_write_scalar(out,element,f,depth+1)) { return 0; } + } + if(list->count) { table_json_line(out,depth); } table_json_put(out,']'); return 1; +} +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; + TableSequence * list=(TableSequence *)storage; char shape=table_json_element_shape(f); + if(graph==NULL || table_json_peek(in)!='[') { in->bad=1; return 0; } + memset(list,0,sizeof(*list)); in->pos++; + for(;;) { + char c=table_json_peek(in); void * element; + if(c==']') { in->pos++; return 1; } + if(c==0) { in->bad=1; return 0; } + element=table_sequence_add(graph->worker,list,f->elem_size); + if(element==NULL) { in->bad=1; return 0; } + if(f->kind==13) { f->table->reset(element); } + if(table_json_value_shape(in)!=shape && !(f->kind==17 && table_json_value_shape(in)=='z')) { + in->report->kind_mismatch++; if(!table_json_skip_value(in,depth+1)) { return 0; } + } else if(!table_json_read_scalar(in,element,f,depth+1)) { return 0; } + c=table_json_peek(in); + if(c==',') { in->pos++; continue; } + if(c==']') { in->pos++; return 1; } + in->bad=1; return 0; + } +} +#endif + +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED TableJsonInteger table_json_interpret_exact( const char * token, int32_t length ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + int32_t i = 0; + if ( i < length && token[i] == '-' ) { out.negative = 1; i++; } // WalkNumber refuses a leading plus + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // leading and trailing zeros stripped, so the last digit kept is significant + int32_t start = 0, end = int_len + frac_len; + int64_t point = (int64_t) int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + const int64_t digits = end - start; + if ( digits == 0 ) { out.negative = 0; return out; } // the value is zero, and -0 IS zero + if ( point < digits ) { out.fractional = 1; return out; } // a significant digit below the point + if ( point > 20 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + for ( int32_t k = start; k < end; k++ ) + { + const uint64_t digit = (uint64_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + out.magnitude = out.magnitude * 10 + digit; + } + for ( int64_t k = digits; k < point; k++ ) // the point's own zeros, which no digit spells + { + if ( out.magnitude > UINT64_MAX / 10 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + out.magnitude *= 10; + } + return out; +} + +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth) +{ + const TableSequence * map=(const TableSequence *)storage; + TableSequenceCursor cursor=table_sequence_cursor(NULL,map,f->elem_size); int32_t i; + const TableFieldInfo * key=&f->table->fields[0]; const TableFieldInfo * value=&f->table->fields[1]; + if(!cursor.ok) { return 0; } table_json_put(out,'{'); + for(i=0;icount;i++) { + const uint8_t * entry=(const uint8_t *)table_sequence_next(&cursor); if(entry==NULL) { return 0; } + if(i) { table_json_put(out,','); } table_json_line(out,depth+1); + if(key->kind==12) { table_json_write_string(out,(const char *)entry+key->offset,table_json_count(entry,key)); } + else { + table_json_put(out,'"'); + if(key->kind>=2 && key->kind<=5) { table_json_write_signed(out,table_json_get_signed(entry+key->offset,key->elem_size)); } + else { table_json_write_unsigned(out,table_json_get_raw(entry+key->offset,key->elem_size)); } + table_json_put(out,'"'); + } + table_json_raw(out,": ",2); if(!table_json_write_field(out,entry,value,depth+1)) { return 0; } + } + if(map->count) { table_json_line(out,depth); } table_json_put(out,'}'); return 1; +} +static SCHEMA_UNUSED int table_json_map_key_value(const char * token,int32_t length,const TableFieldInfo * key,int64_t * value,int * fits) +{ + TableReport scratch={0}; TableJsonIn probe; TableJsonInteger number; int integral=0,moved=0; + memset(&probe,0,sizeof(probe)); probe.text=token; probe.size=length; probe.report=&scratch; *fits=0; + table_json_space(&probe); if(probe.pos!=0 || !table_json_walk_number(&probe,&integral) || probe.pos!=length) { return 0; } + number=table_json_interpret_exact(token,length); if(number.fractional || number.saturated) { return 1; } + *value=table_json_integer_in_domain(number,key->kind>=2 && key->kind<=5,(int32_t)key->elem_size,&moved); *fits=!moved; return 1; +} +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; TableSequence * map=(TableSequence *)storage; + const TableFieldInfo * key=&f->table->fields[0]; const TableFieldInfo * value=&f->table->fields[1]; char shape=table_json_shape(value); + if(graph==NULL || table_json_peek(in)!='{' || depth+1>kTableJsonMaxDepth) { in->bad=1; return 0; } + memset(map,0,sizeof(*map)); in->pos++; + for(;;) { + char c=table_json_peek(in),token[kTableJsonMaxKey]; int32_t length=0,before; int64_t number=0; int place=1; void * entry; + TableReport scratch={0}; TableJsonIn scan=*in; scan.report=&scratch; + if(c=='}') { in->pos++; return 1; } if(c==0) { in->bad=1; return 0; } + if(!table_json_scan_string(&scan,token,kTableJsonMaxKey-1,&length)) { in->pos=scan.pos; in->bad=1; return 0; } + in->pos=scan.pos; token[length]=0; + if(table_json_peek(in)!=':') { in->bad=1; return 0; } in->pos++; + if(key->kind==12) { + if(scratch.clamped || length>key->array_bound) { in->report->clamped++; place=0; } + } else { + int fits=0; + if(scratch.clamped) { in->report->kind_mismatch++; place=0; } + else if(!table_json_map_key_value(token,length,key,&number,&fits)) { in->bad=1; return 0; } + else if(!fits) { in->report->kind_mismatch++; place=0; } + } + before=map->count; entry=place ? f->place(graph->worker,map,token,length,number) : NULL; + if(place && entry==NULL) { in->bad=1; return 0; } + if(entry!=NULL && map->count==before) { in->report->duplicate++; } + c=table_json_value_shape(in); + if(entry==NULL) { if(!table_json_skip_value(in,depth+1)) { return 0; } } + else if(c!=shape && !(value->kind==17 && !value->is_array && c=='z')) { + in->report->kind_mismatch++; if(!table_json_skip_value(in,depth+1)) { return 0; } + } else if(!table_json_read_field(in,entry,value,depth+1)) { return 0; } + c=table_json_peek(in); if(c==',') { in->pos++; continue; } if(c=='}') { in->pos++; return 1; } + in->bad=1; return 0; + } +} +#endif static const TableFieldInfo schema_graphdemo_tally_fields_[] = { - { "hits", "hits", "int32", 0xb723, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Tally, hits ), (uint32_t) sizeof( ( (Tally *) 0 )->hits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10000.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "hits", "hits", "int32", 0x732dfbcc9b0cf0bbull, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Tally, hits ), (uint32_t) sizeof( ( (Tally *) 0 )->hits ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 10000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_tally_info_ = { "Tally", (uint32_t) sizeof( Tally ), 1, schema_graphdemo_tally_fields_, schema_graphdemo_tally_reset_raw_, 0, TableDocNone, 0, NULL }; static const TableFieldInfo schema_graphdemo_marker_fields_[] = { - { "label", "label", "string", 0xe16a, 12, 0, 0, 1, 0, 8, (uint32_t) offsetof( Marker, label ), (uint32_t) sizeof( ( (Marker *) 0 )->label ), (uint32_t) offsetof( Marker, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "note", "note", "Tally", 0x9da7, 13, 0, 1, 0, 0, 0, (uint32_t) offsetof( Marker, note ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_tally_info_, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "label", "label", "string", 0x39f7fcec8fcb623dull, 12, 0, 0, 1, 0, 8, (uint32_t) offsetof( Marker, label ), (uint32_t) sizeof( ( (Marker *) 0 )->label ), (uint32_t) offsetof( Marker, label_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "note", "note", "Tally", 0x3bf8fbbad1587cddull, 17, 0, 1, 0, 0, 0, (uint32_t) offsetof( Marker, note ), (uint32_t) sizeof( TableRef ), 0xffffffffu, 0xffffffffu, &schema_graphdemo_tally_info_, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_marker_info_ = { "Marker", (uint32_t) sizeof( Marker ), 2, schema_graphdemo_marker_fields_, schema_graphdemo_marker_reset_raw_, 1, TableDocNone, 0, NULL }; @@ -1751,3 +2798,13 @@ int64_t schema_graphdemo_tally_to_json_( const Tally * value, char * buffer, int return table_json_write( value, &schema_graphdemo_tally_info_, buffer, capacity ); } +int schema_graphdemo_marker_from_json_( MarkerBuilder * builder, const char * text, int64_t bytes, TableReport * report ) +{ + return table_json_read_graph(&builder->main,marker_builder_root(builder),&schema_graphdemo_marker_info_,text,bytes,report); +} + +int64_t schema_graphdemo_marker_to_json_( const Marker * value, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + return table_json_write_graph(value,&schema_graphdemo_marker_info_,buffer,capacity,allocator); +} + diff --git a/testdata/golden/tables/pointers-c/MarksTable.h b/testdata/golden/tables/pointers-c/MarksTable.h index 2c838d7ab..36b9d4fe2 100644 --- a/testdata/golden/tables/pointers-c/MarksTable.h +++ b/testdata/golden/tables/pointers-c/MarksTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package graphdemo — protocol id 0x36ff4b4cd88bf37a (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_GRAPHDEMO_MARKSTABLE_H #define SCHEMA_GRAPHDEMO_MARKSTABLE_H @@ -13,8 +13,8 @@ #include #include /* offsetof, for the reflection descriptors */ #include /* memcpy, memset — the prefill and the wire's raw moves */ -#include /* the arena's segments (the AUTHORING path may allocate) */ #include /* the keyed accessor's None refusal, in a debug build */ +#include /* and its abort, which NDEBUG does not remove */ #include "Marks.h" @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int is_pointer; /* a *T pointer field: storage is an 8-byte TableRef; the target is a table */ @@ -184,6 +195,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -211,6 +224,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -237,157 +252,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[48]; + uint32_t slots[128], positions[48]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + struct TableNumbering * nodes; } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (128-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(128-1); + } + if(v->count==48) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + struct TableNodeMap * nodes; + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; } - return 0; + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; } +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; +} + +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -397,6 +532,94 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_GRAPHDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + + +#ifndef SCHEMA_TABLE_ALLOCATOR_RUNTIME +#define SCHEMA_TABLE_ALLOCATOR_RUNTIME +#ifndef schema_allocate +#include +#define schema_allocate(bytes) calloc(1,(size_t)(bytes)) +#endif +#ifndef schema_release +#include +#define schema_release(pointer) free(pointer) +#endif +typedef struct TableAllocator +{ + void * (*alloc)(void * context,int64_t bytes); + void (*free)(void * context,void * pointer); + void * context; +} TableAllocator; +static SCHEMA_UNUSED void * table_default_allocate(void * context,int64_t bytes) +{ (void)context; return bytes>0 && (uint64_t)bytes<=SIZE_MAX ? schema_allocate(bytes) : NULL; } +static SCHEMA_UNUSED void table_default_release(void * context,void * pointer) +{ (void)context; schema_release(pointer); } +static SCHEMA_UNUSED TableAllocator table_default_allocator(void) +{ + TableAllocator allocator={table_default_allocate,table_default_release,NULL}; return allocator; +} +static SCHEMA_UNUSED void * table_allocate(TableAllocator allocator,int64_t bytes) +{ + if(allocator.alloc==NULL || allocator.free==NULL || bytes<=0 || (uint64_t)bytes>SIZE_MAX) { return NULL; } + return allocator.alloc(allocator.context,bytes); +} +static SCHEMA_UNUSED void table_release(TableAllocator allocator,void * pointer) +{ if(allocator.free!=NULL) { allocator.free(allocator.context,pointer); } } +#endif #ifndef SCHEMA_GRAPHDEMO_TABLE_ARENA #define SCHEMA_GRAPHDEMO_TABLE_ARENA @@ -563,10 +786,11 @@ typedef struct TableArena { TableAtomicPtr segments[ kTableMaxSegments ]; TableAtomicU32 cursor; /* (segment << kTableSegmentBits) | bytes handed out */ + TableAllocator allocator; int locked; /* MONOTONIC: Lock is one-way, there is no unlock */ } TableArena; -static SCHEMA_UNUSED void table_arena_init( TableArena * arena ) +static SCHEMA_UNUSED void table_arena_init_with_allocator( TableArena * arena, TableAllocator allocator ) { uint32_t i; for ( i = 0; i < kTableMaxSegments; i++ ) @@ -574,9 +798,12 @@ static SCHEMA_UNUSED void table_arena_init( TableArena * arena ) table_atomic_store_ptr( &arena->segments[i], (uint8_t *) NULL ); } table_atomic_store32( &arena->cursor, 0 ); - arena->locked = 0; + arena->locked = 0; arena->allocator=allocator; } +static SCHEMA_UNUSED void table_arena_init(TableArena * arena) +{ table_arena_init_with_allocator(arena,table_default_allocator()); } + static SCHEMA_UNUSED void table_arena_shutdown( TableArena * arena ) { uint32_t i; @@ -586,7 +813,7 @@ static SCHEMA_UNUSED void table_arena_shutdown( TableArena * arena ) if ( segment != NULL ) { uint8_t * expected = segment; - if ( table_arena_cas_ptr( &arena->segments[i], &expected, (uint8_t *) NULL ) ) { free( segment ); } + if ( table_arena_cas_ptr( &arena->segments[i], &expected, (uint8_t *) NULL ) ) { table_release(arena->allocator,segment); } } } table_atomic_store32( &arena->cursor, 0 ); @@ -621,12 +848,12 @@ static SCHEMA_UNUSED uint32_t table_arena_grab_slab( TableArena * arena ) region. It costs nothing measurable: a fresh segment is untouched pages either way, and calloc has the kernel hand them over zeroed. */ - uint8_t * memory = (uint8_t *) calloc( 1, kTableSegmentSize ); + uint8_t * memory = (uint8_t *) table_allocate(arena->allocator,kTableSegmentSize); uint8_t * expected = NULL; if ( memory == NULL ) { return kTableAllocFailed; } if ( !table_arena_cas_ptr( &arena->segments[segment], &expected, memory ) ) { - free( memory ); /* another worker published this segment first */ + table_release(arena->allocator,memory); /* another worker published this segment first */ } } if ( table_arena_cas32( &arena->cursor, &cursor, cursor + kTableSlabBytes ) ) @@ -672,24 +899,44 @@ static SCHEMA_UNUSED TableWorker table_worker_make( TableArena * arena ) arena offset, or kTableAllocFailed. It is the untyped half of every generated _emplace: the type's own size and its reset stay in the generated code, where they can be spelled. */ -static SCHEMA_UNUSED uint32_t table_worker_bump( TableWorker * worker, uint32_t bytes ) +static SCHEMA_UNUSED uint32_t table_arena_grab_span( TableArena * arena, int64_t bytes ) +{ + uint32_t spanned; + if(bytes<=0 || bytes>((int64_t)kTableMaxSegments-2)*kTableSegmentSize) { return kTableAllocFailed; } + spanned=(uint32_t)((bytes+kTableSegmentSize-1)>>kTableSegmentBits); + for(;;) + { + uint32_t cursor=table_atomic_load32(&arena->cursor); + uint32_t start=(cursor>>kTableSegmentBits)+1; + uint32_t next; + uint8_t * memory; + if(start+spanned>=kTableMaxSegments) { return kTableAllocFailed; } + next=(start+spanned)<cursor,&cursor,next)) { continue; } + memory=(uint8_t *)table_allocate(arena->allocator,bytes); + if(memory==NULL) { return kTableAllocFailed; } + table_atomic_store_ptr(&arena->segments[start],memory); + return start<arena == NULL || worker->arena->locked ) { return kTableAllocFailed; } - bytes = table_align_up( bytes ); - if ( bytes > kTableSlabBytes ) { return kTableAllocFailed; } /* a node larger than a slab: refused, never split */ - if ( worker->end == 0 || worker->next + bytes > worker->end ) + if(worker->arena==NULL || worker->arena->locked || bytes<=0 || bytes>INT64_MAX-kTableAlign) { return kTableAllocFailed; } + bytes=table_align_up64(bytes); + if(bytes>kTableSlabBytes) { return table_arena_grab_span(worker->arena,bytes); } + if(worker->end==0 || (uint64_t)worker->next+(uint64_t)bytes>worker->end || (worker->next==0 && bytes+kTableAlign>kTableSlabBytes)) { - uint32_t offset = table_arena_grab_slab( worker->arena ); - if ( offset == kTableAllocFailed ) { return kTableAllocFailed; } - worker->next = offset; - worker->end = offset + kTableSlabBytes; - if ( worker->next == 0 ) { worker->next = kTableAlign; } /* offset 0 is null: the arena's head stays reserved */ + uint32_t offset=table_arena_grab_slab(worker->arena); + if(offset==kTableAllocFailed) { return kTableAllocFailed; } + worker->next=offset; worker->end=offset+kTableSlabBytes; + if(worker->next==0) { worker->next=kTableAlign; } + if((uint64_t)worker->next+(uint64_t)bytes>worker->end) { return table_arena_grab_span(worker->arena,bytes); } } - at = worker->next; - worker->next += bytes; - return at; + at=worker->next; worker->next+=(uint32_t)bytes; return at; } +static SCHEMA_UNUSED uint32_t table_worker_bump( TableWorker * worker, uint32_t bytes ) +{ return table_worker_alloc(worker,bytes); } /* ---- the resolution context: which encoding a walk is reading ---- @@ -727,6 +974,469 @@ typedef struct TableSink #endif /* SCHEMA_GRAPHDEMO_TABLE_ARENA */ +#ifndef SCHEMA_TABLE_BLOB_RUNTIME +#define SCHEMA_TABLE_BLOB_RUNTIME +typedef struct TableBlob { uint32_t length, zero; } TableBlob; +typedef struct TableBytesView { const uint8_t * data; int64_t length; } TableBytesView; +typedef struct TableStringView { const char * data; int64_t length; } TableStringView; +#define kTableBytesTypeId UINT64_C(0x2f2ec0474f1c4fe4) +#define kTableStringTypeId UINT64_C(0x704be0d8faaffc58) + +static SCHEMA_UNUSED int64_t table_blob_storage( int64_t length, int terminated ) +{ + if ( length < 0 || (uint64_t)length > UINT32_MAX ) { return -1; } + return table_align_up64(8+length+(terminated ? 1 : 0)); +} + +static SCHEMA_UNUSED const TableBlob * table_blob_at( const TableCtx * ctx, const TableRef * ref ) +{ + if ( ref->value == 0 ) { return NULL; } + return (const TableBlob *)(const void *)(ctx != NULL && ctx->arena != NULL + ? table_arena_at(ctx->arena,(uint32_t)ref->value) : (const uint8_t *)(const void *)ref+ref->value); +} +static SCHEMA_UNUSED TableBytesView table_bytes_at( const TableCtx * ctx, const TableRef * ref ) +{ + const TableBlob * blob=table_blob_at(ctx,ref); + TableBytesView view={NULL,0}; + if(blob!=NULL) { view.data=(const uint8_t *)(blob+1); view.length=blob->length; } return view; +} +static SCHEMA_UNUSED TableStringView table_string_at( const TableCtx * ctx, const TableRef * ref ) +{ + const TableBlob * blob=table_blob_at(ctx,ref); + TableStringView view={NULL,0}; + if(blob!=NULL) { view.data=(const char *)(blob+1); view.length=blob->length; } return view; +} +static SCHEMA_UNUSED TableBlob * table_blob_emplace( TableWorker * worker, TableRef * ref, int64_t length, int terminated ) +{ + int64_t bytes=table_blob_storage(length,terminated); + uint32_t at; + TableBlob * blob; + if(bytes<0) { return NULL; } + at=table_worker_alloc(worker,bytes); + if(at==kTableAllocFailed) { return NULL; } + blob=(TableBlob *)(void *)table_arena_at(worker->arena,at); + blob->length=(uint32_t)length; blob->zero=0; ref->value=at; return blob; +} +static SCHEMA_UNUSED uint8_t * table_bytes_emplace( TableWorker * worker, TableRef * ref, int64_t length ) +{ + TableBlob * blob=table_blob_emplace(worker,ref,length,0); + return blob!=NULL ? (uint8_t *)(blob+1) : NULL; +} +static SCHEMA_UNUSED char * table_string_emplace( TableWorker * worker, TableRef * ref, const char * text, int64_t length ) +{ + TableBlob * blob=table_blob_emplace(worker,ref,length,1); + char * data; + if(blob==NULL) { return NULL; } + data=(char *)(blob+1); if(text!=NULL && length>0) { memcpy(data,text,(size_t)length); } data[length]=0; return data; +} +#endif + +#ifndef SCHEMA_TABLE_GRAPH_RUNTIME +#define SCHEMA_TABLE_GRAPH_RUNTIME +typedef struct TableNumbering TableNumbering; +typedef struct TableNodeType +{ + uint64_t id; + int64_t size; + int (*save)( TableWriter * w, const void * value ); + int (*edges)( TableNumbering * numbering, const void * value ); + int blob; /* 0 table, 1 bytes, 2 terminated UTF-8 */ + int (*extent)(TableNumbering *,const void *,uint8_t *,int64_t *); + int64_t (*wire_storage)(const TableReader *); + int64_t alignment; + int has_extent; + int (*cook_body)(struct TableNumbering *,uint8_t *,const void *,int); + int (*cook_extent)(TableNumbering *,const void *,uint8_t *,uint8_t *,int64_t *,int); +} TableNodeType; + +typedef struct TableNodeEntry +{ + const void * value; + const TableNodeType * type; + uint64_t packed_offset; + int open; +} TableNodeEntry; + +typedef struct TableNodeWork +{ + const void * value; + const TableNodeType * type; + uint64_t index; + const TableRef * slot; + int close; +} TableNodeWork; + +struct TableNumbering +{ + const TableCtx * ctx; + uint8_t * pack_base; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + TableSequenceOrder * orders; +#endif + TableAllocator allocator; + TableNodeEntry * entries; + uint64_t count, capacity; + uint64_t * slots; + uint64_t slot_capacity; + TableNodeWork * work; + uint64_t work_count, work_capacity; +}; + +static SCHEMA_UNUSED int64_t table_node_storage( TableNumbering * n, const TableNodeType * type, const void * value ) +{ + int64_t at=type->size; + if(type->blob) { return table_blob_storage(((const TableBlob *)value)->length,type->blob==2); } + if(type->has_extent) { + int64_t extent=0; + if(!type->cook_extent(n,value,NULL,NULL,&extent,1) || at>INT64_MAX-kTableAlign) { return -1; } + at=table_align_up64(at); if(extent>INT64_MAX-at) { return -1; } at+=extent; + } + if(at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int64_t table_node_wire_storage( const TableNodeType * type, const TableReader * body ) +{ + if(type->blob && body->size>UINT32_MAX) { + body->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return -1; + } + return type->blob ? table_blob_storage(body->size,type->blob==2) : type->wire_storage(body); +} + +static SCHEMA_UNUSED int table_blob_save( TableWriter * w, const void * value ) +{ + const TableBlob * blob=(const TableBlob *)value; + table_writer_raw(w,blob+1,blob->length); return !w->overflow; +} +static SCHEMA_UNUSED int table_blob_edges( TableNumbering * n, const void * value ) +{ (void)n; (void)value; return 1; } +static const TableNodeType table_bytes_node_type = { kTableBytesTypeId, 0, table_blob_save, table_blob_edges, 1, NULL, NULL, 8, 0, NULL, NULL }; +static const TableNodeType table_string_node_type = { kTableStringTypeId, 0, table_blob_save, table_blob_edges, 2, NULL, NULL, 8, 0, NULL, NULL }; + + +static SCHEMA_UNUSED uint64_t table_number_hash( const void * value ) +{ + uint64_t x = (uint64_t)(uintptr_t)value; + x ^= x >> 33; x *= UINT64_C(0xff51afd7ed558ccd); + x ^= x >> 33; x *= UINT64_C(0xc4ceb9fe1a85ec53); + return x ^ (x >> 33); +} + +static SCHEMA_UNUSED void * table_number_grow( TableAllocator allocator, void * memory, uint64_t * capacity, size_t width ) +{ + uint64_t next = *capacity ? *capacity * 2 : 64; + void * grown; + if ( next < *capacity || next > INT64_MAX / width ) { return 0; } + grown = table_allocate(allocator,(int64_t)next*(int64_t)width); + if ( grown == NULL ) { return 0; } + if(memory!=NULL) { memcpy(grown,memory,(size_t)*capacity*width); } + table_release(allocator,memory); *capacity = next; return grown; +} + +static SCHEMA_UNUSED int table_number_rehash( TableNumbering * n ) +{ + uint64_t capacity = n->slot_capacity ? n->slot_capacity * 2 : 128; + uint64_t * slots; + uint64_t i; + if ( capacity < n->slot_capacity || capacity > INT64_MAX / sizeof(uint64_t) ) { return 0; } + slots = (uint64_t *)table_allocate(n->allocator,(int64_t)capacity*sizeof(uint64_t)); + if ( slots == NULL ) { return 0; } + for ( i = 0; i < n->count; i++ ) + { + uint64_t at = table_number_hash(n->entries[i].value) & (capacity-1); + while ( slots[at] ) { at = (at+1) & (capacity-1); } + slots[at] = i+1; + } + table_release(n->allocator,n->slots); n->slots = slots; n->slot_capacity = capacity; return 1; +} + +static SCHEMA_UNUSED uint64_t table_number_find( const TableNumbering * n, const void * value ) +{ + uint64_t at; + if ( value == NULL || n == NULL || n->slot_capacity == 0 ) { return 0; } + at = table_number_hash(value) & (n->slot_capacity-1); + while ( n->slots[at] ) + { + uint64_t index = n->slots[at]; + if ( n->entries[index-1].value == value ) { return index; } + at = (at+1) & (n->slot_capacity-1); + } + return 0; +} + +static SCHEMA_UNUSED int table_number_edge( TableNumbering * n, const void * value, const TableNodeType * type ) +{ + TableNodeWork entry; + if ( value == NULL ) { return 1; } + if ( n->work_count == n->work_capacity ) + { + void * grown = table_number_grow(n->allocator,n->work,&n->work_capacity,sizeof(TableNodeWork)); + if ( grown == NULL ) { return 0; } + n->work = (TableNodeWork *)grown; + } + entry.value = value; entry.type = type; entry.index = 0; entry.close = 0; entry.slot = NULL; + n->work[n->work_count++] = entry; return 1; +} + +static SCHEMA_UNUSED void table_number_dispose( TableNumbering * n ) +{ +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + table_sequence_orders_release(n->allocator,n->orders); +#endif + table_release(n->allocator,n->entries); table_release(n->allocator,n->slots); table_release(n->allocator,n->work); + memset(n,0,sizeof(*n)); +} + +static SCHEMA_UNUSED int table_number_build( TableNumbering * n, const TableCtx * ctx, + const void * root, const TableNodeType * type, TableAllocator allocator ) +{ + memset(n,0,sizeof(*n)); n->ctx = ctx; n->allocator=allocator; + if ( root == NULL || !table_number_edge(n,root,type) ) { return 0; } + while ( n->work_count ) + { + TableNodeWork visit = n->work[--n->work_count]; + uint64_t index, at, first, last; + TableNodeEntry entry; + if ( visit.close ) { n->entries[visit.index].open = 0; continue; } + index = table_number_find(n,visit.value); + if ( index ) + { + if ( n->entries[index-1].open || n->entries[index-1].type->id != visit.type->id ) { return 0; } + continue; + } + if ( n->slot_capacity == 0 || n->count >= n->slot_capacity/2 ) + { if ( !table_number_rehash(n) ) { return 0; } } + if ( n->count == n->capacity ) + { + void * grown = table_number_grow(n->allocator,n->entries,&n->capacity,sizeof(TableNodeEntry)); + if ( grown == NULL ) { return 0; } + n->entries = (TableNodeEntry *)grown; + } + entry.value = visit.value; entry.type = visit.type; entry.packed_offset = 0; entry.open = 1; + visit.index = n->count; visit.close = 1; + n->entries[n->count++] = entry; + at = table_number_hash(visit.value) & (n->slot_capacity-1); + while ( n->slots[at] ) { at = (at+1) & (n->slot_capacity-1); } + n->slots[at] = n->count; + if ( n->work_count == n->work_capacity ) + { + void * grown = table_number_grow(n->allocator,n->work,&n->work_capacity,sizeof(TableNodeWork)); + if ( grown == NULL ) { return 0; } + n->work = (TableNodeWork *)grown; + } + n->work[n->work_count++] = visit; + first = n->work_count; + if ( !visit.type->edges(n,visit.value) ) { return 0; } + last = n->work_count; + // Callbacks emit edges in declaration order. Reverse only this set + // so the LIFO work stack visits its first edge first, before siblings. + while ( first < last && first < --last ) + { + TableNodeWork swap = n->work[first]; + n->work[first++] = n->work[last]; n->work[last] = swap; + } + } + table_release(n->allocator,n->work); n->work = NULL; n->work_capacity = 0; + return 1; +} + + +static SCHEMA_UNUSED const void * table_ref_at( const TableCtx * ctx, const TableRef * slot ) +{ + if ( slot->value == 0 ) { return NULL; } + if ( ctx != NULL && ctx->arena != NULL ) { return table_arena_at(ctx->arena,(uint32_t)slot->value); } + return (const uint8_t *)(const void *)slot + slot->value; +} + +static SCHEMA_UNUSED int table_number_slot( TableNumbering * n, const TableRef * slot, const TableNodeType * type ) +{ + const void * value = table_ref_at(n->ctx,slot); + if ( value == NULL ) { return 1; } + if ( !table_number_edge(n,value,type) ) { return 0; } + n->work[n->work_count-1].slot = slot; + return 1; +} + +static SCHEMA_UNUSED int table_graph_records( TableWriter * w ) +{ + uint64_t i; + const TableNumbering * n = w->nodes; + table_writer_leb(w,n->count-1); + for ( i=1; icount; i++ ) + { + const TableNodeEntry * entry=&n->entries[i]; + table_writer_id(w,entry->type->id); + if ( w->buffer == NULL ) + { + int64_t begin=w->offset; + if ( !entry->type->save(w,entry->value) ) { return 0; } + table_writer_leb(w,(uint64_t)(w->offset-begin)); + } + else + { + TableWriter probe=table_writer_probe(w); + if ( !entry->type->save(&probe,entry->value) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb(w,(uint64_t)probe.offset); + if ( !entry->type->save(w,entry->value) ) { return 0; } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int64_t table_graph_save( const TableCtx * ctx, const void * root, + const TableNodeType * type, uint8_t * buffer, int64_t capacity, TableAllocator allocator ) +{ + TableNumbering numbering; + TableWriteIds vocabulary; + TableWriter w=table_writer_make(buffer,capacity,&vocabulary); + int64_t result=-1; + if ( !table_number_build(&numbering,ctx,root,type,allocator) ) { table_number_dispose(&numbering); return -1; } + w.nodes=&numbering; + table_writer_put8(&w,1); + if ( !type->save(&w,root) ) { goto done; } + w.offset--; // the root terminator follows the numbering's final field + if ( numbering.count>1 ) + { + table_writer_id(&w,UINT64_MAX); table_writer_put8(&w,12); + if ( buffer == NULL ) + { + int64_t begin=w.offset; + if ( !table_graph_records(&w) ) { goto done; } + table_writer_leb(&w,(uint64_t)(w.offset-begin)); + } + else + { + TableWriter probe=table_writer_probe(&w); + if ( !table_graph_records(&probe) ) { goto done; } + table_writer_rewind(&probe); table_writer_leb(&w,(uint64_t)probe.offset); + if ( !table_graph_records(&w) ) { goto done; } + } + } + table_writer_put8(&w,0); table_writer_finish(&w); + if ( !w.overflow ) { result=w.offset; } +done: + table_number_dispose(&numbering); return result; +} + +static SCHEMA_UNUSED uint8_t * table_graph_pack( const TableCtx * ctx, const void * root, + const TableNodeType * type, int64_t * bytes ) +{ + TableNumbering n; + uint64_t i; + int64_t total=0; + uint8_t * packed=NULL; + *bytes=0; + if ( !table_number_build(&n,ctx,root,type,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()) ) { table_number_dispose(&n); return NULL; } + for ( i=0; iINT64_MAX-total ) { goto done; } + n.entries[i].packed_offset=(uint64_t)total; total+=size; + } + if ( (uint64_t)total>SIZE_MAX ) { goto done; } + packed=(uint8_t *)table_allocate(n.allocator,total); + if ( packed==NULL ) { goto done; } + memset(packed,0,(size_t)total); + for ( i=0; ipacked_offset; + n.pack_base=packed; + if(entry->type->blob) { + memcpy(target,entry->value,(size_t)(8+(int64_t)((const TableBlob *)entry->value)->length)); + } else { + const uint16_t one=1; + int order=*(const uint8_t *)(const void *)&one ? 1 : 2; + int64_t at=0; + if(!entry->type->cook_body(&n,target,entry->value,order) || + (entry->type->has_extent && !entry->type->cook_extent(&n,entry->value,target,target+table_align_up64(entry->type->size),&at,order))) { + table_release(n.allocator,packed); packed=NULL; goto done; + } + } + } + *bytes=total; +done: + table_number_dispose(&n); return packed; +} +typedef struct TableNodeDirEntry { uint64_t offset, type_id; } TableNodeDirEntry; +typedef struct TableNodeMap +{ + uint8_t * base; + const TableNodeDirEntry * entries; + uint64_t count; + int good, arena, refused; + TableSink * sink; +} TableNodeMap; + +static SCHEMA_UNUSED void table_node_resolve( const TableNodeMap * nodes, TableRef * slot, + uint64_t index, uint64_t target, TableReport * report ) +{ + const TableNodeDirEntry * entry; + slot->value = 0; + if ( index == 0 || nodes == NULL || !nodes->good ) { return; } + if ( index > nodes->count ) { report->malformed = 1; return; } + entry = &nodes->entries[index-1]; + if ( entry->offset == UINT64_MAX ) { return; } + if ( entry->type_id != target ) { report->kind_mismatch++; return; } + slot->value = nodes->arena ? (int64_t)entry->offset + : (int64_t)((nodes->base + entry->offset) - (uint8_t *)(void *)slot); +} + +typedef struct TableNodeScan +{ + TableReader fields, payload; + uint64_t declared, records; + int opened, present, malformed; +} TableNodeScan; + +static SCHEMA_UNUSED TableNodeScan table_node_scan_begin( const TableReader * root ) +{ + TableNodeScan scan; + memset(&scan,0,sizeof(scan)); scan.fields = *root; scan.fields.offset = 0; + return scan; +} + +static SCHEMA_UNUSED int table_node_scan_open( TableNodeScan * scan ) +{ + TableReader * fields = &scan->fields; + if ( scan->opened ) { return 0; } + scan->opened = 1; + for ( ;; ) + { + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb(fields,&ref) || ref == 0 || ref > fields->id_count ) { break; } + id = table_reader_id_at(fields,ref); + if ( !table_reader_has(fields,1) ) { break; } + kind = table_reader_get8(fields); + if ( id == UINT64_MAX ) + { + scan->present = 1; + if ( kind != 12 || !table_reader_span(fields,&scan->payload) ) + { scan->malformed = 1; return 0; } + } + else if ( !table_reader_skip(fields,kind) ) { break; } + } + if ( !scan->present ) { return 0; } + if ( !table_reader_leb(&scan->payload,&scan->declared) ) { scan->malformed = 1; return 0; } + return 1; +} + +static SCHEMA_UNUSED int table_node_scan_next( TableNodeScan * scan, uint64_t * type_id, TableReader * body ) +{ + uint64_t ref; + if ( !scan->opened && !table_node_scan_open(scan) ) { return 0; } + if ( scan->malformed || scan->payload.offset >= scan->payload.size ) { return 0; } + if ( !table_reader_leb(&scan->payload,&ref) || ref == 0 || ref > scan->payload.id_count || + !table_reader_span(&scan->payload,body) ) { scan->malformed = 1; return 0; } + *type_id = table_reader_id_at(&scan->payload,ref); scan->records++; + return 1; +} + +static SCHEMA_UNUSED int table_node_scan_whole( const TableNodeScan * scan ) +{ + return !scan->malformed && (!scan->present || scan->declared == scan->records); +} +#endif + #ifndef SCHEMA_GRAPHDEMO_BUILD_VERSION #define SCHEMA_GRAPHDEMO_BUILD_VERSION @@ -838,25 +1548,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -866,66 +1576,1398 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_GRAPHDEMO_TABLE_COOK */ -/* table Tally — TABLE-wire storage: relocatable, bounded. C has no member - initializers, so the declared defaults live in tally_reset and nowhere - else — one definition of what a default is (docs/SPEC-TABLES.md) */ -typedef struct Tally { - int32_t hits; -} Tally; - -/* table Marker — TABLE-wire storage: relocatable, bounded. C has no member - initializers, so the declared defaults live in marker_reset and nowhere - else — one definition of what a default is (docs/SPEC-TABLES.md) */ -typedef struct Marker { - char label[8 + 1]; /* string(8): N + 1 for the terminator the wire does not carry */ - int32_t label_length; - TableRef note; /* *Tally — null until assigned */ -} Marker; - -/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ - -static SCHEMA_UNUSED void tally_reset( Tally * value ); -static SCHEMA_UNUSED void schema_graphdemo_tally_reset_raw_( void * storage ); -static SCHEMA_UNUSED void marker_reset( Marker * value ); -static SCHEMA_UNUSED void schema_graphdemo_marker_reset_raw_( void * storage ); - -static SCHEMA_UNUSED void tally_reset( Tally * value ) +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) { - value->hits = 0; + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } +} +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused;TableNodeMap * nodes; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + +#ifndef SCHEMA_TABLE_COOK_GRAPH_RUNTIME +#define SCHEMA_TABLE_COOK_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_cook_ref(TableNumbering * n,uint8_t * at,const TableRef * slot,int order) +{ + const void * pointee=table_ref_at(n->ctx,slot); + uint64_t index=table_number_find(n,pointee); + if(pointee!=NULL && index==0) { return 0; } + table_cook_put(at,index ? n->entries[index-1].packed_offset-(uint64_t)(at-n->pack_base) : 0,8,order); + return 1; +} +static SCHEMA_UNUSED int64_t table_cook_layout(TableNumbering * n,int64_t * alignment,int64_t * data_bytes) +{ + uint64_t i; + int64_t total=0, align=8; + for(i=0;icount;i++) { + TableNodeEntry * entry=&n->entries[i]; const TableNodeType * type=entry->type; + int64_t size=type->size, node_align=type->alignment; + if(type->blob) { size=8+(int64_t)((const TableBlob *)entry->value)->length+(type->blob==2); } + else if(type->has_extent) { + int64_t extent=0; + if(!type->cook_extent(n,entry->value,NULL,NULL,&extent,TableByteOrder_Little) || size>INT64_MAX-7) { return -1; } + size=(size+7)&~INT64_C(7); if(extent>INT64_MAX-size) { return -1; } size+=extent; + } + if(node_align<1 || total>INT64_MAX-(node_align-1)) { return -1; } + total=(total+node_align-1)&~(node_align-1); + entry->packed_offset=(uint64_t)total; + if(size<0 || size>INT64_MAX-total) { return -1; } total+=size; + if(node_align>align) { align=node_align; } + } + if(total>INT64_MAX-(align-1)) { return -1; } total=(total+align-1)&~(align-1); + *alignment=align; *data_bytes=total; + if(total>INT64_MAX-64 || n->count>(uint64_t)(INT64_MAX-64-total)/16) { return -1; } + return 64+total+(int64_t)n->count*16; +} +static SCHEMA_UNUSED int64_t table_graph_cook(const TableCtx * ctx,const void * root,const TableNodeType * type, + void * output,uint64_t capacity,int order,TableAllocator allocator,uint64_t version,int measure) +{ + TableNumbering n; int64_t result=-1,align=8,bytes=0,need; uint64_t i; uint8_t * raw=(uint8_t *)output; + if(!table_number_build(&n,ctx,root,type,allocator)) { table_number_dispose(&n); return -1; } + need=table_cook_layout(&n,&align,&bytes); if(need<0) { goto done; } + if(measure) { result=need; goto done; } + if(raw==NULL || (uint64_t)need>capacity || (uint64_t)need>SIZE_MAX) { goto done; } + memset(raw,0,(size_t)need); n.pack_base=raw+64; + for(i=0;ipacked_offset; + if(entry->type->blob) { + const TableBlob * blob=(const TableBlob *)entry->value; + table_cook_put(at,blob->length,4,order); table_cook_bytes(at+8,blob+1,blob->length,blob->length); + } else { + int64_t extent=0; + if(!entry->type->cook_body(&n,at,entry->value,order)) { goto done; } + if(entry->type->has_extent) { + int64_t expected=0,record=(entry->type->size+7)&~INT64_C(7); + if(!entry->type->cook_extent(&n,entry->value,NULL,NULL,&expected,order) || + !entry->type->cook_extent(&n,entry->value,at,at+record,&extent,order) || extent!=expected) { goto done; } + } + } + table_cook_put(n.pack_base+bytes+i*16,entry->packed_offset,8,order); + table_cook_put(n.pack_base+bytes+i*16+8,entry->type->id,8,order); + } + /* A failed body must never leave a recognizable cook header. */ + table_cook_header(raw,version,bytes,(int64_t)n.count,align,order); result=need; +done: + table_number_dispose(&n); return result; +} +#endif + +#ifndef SCHEMA_TABLE_RETAIN_RUNTIME +#define SCHEMA_TABLE_RETAIN_RUNTIME +#define SCHEMA_TABLE_RETAIN_DEPTH_MAX 1 +#define SCHEMA_TABLE_RETAIN_WALK_MAX 64 +#define SCHEMA_TABLE_RETAIN_HEADER 26 + +typedef struct TableRetainId { uint64_t id; int32_t slot; } TableRetainId; +typedef struct TableRetain { + uint8_t * bytes; int64_t capacity,used; + TableRetainId * ids; int32_t id_capacity,id_used,count; + const uint8_t * base; const TableNodeDirEntry * directory; int64_t directory_count; +} TableRetain; +typedef struct TableRetainStep {uint32_t ordinal,index;} TableRetainStep; +typedef struct TableRetainPath { + const void * at; uint32_t node; int32_t depth; + TableRetainStep steps[SCHEMA_TABLE_RETAIN_DEPTH_MAX]; +} TableRetainPath; +typedef struct TableRetainIds { + TableWriteIds known; + int32_t slots[sizeof(((TableWriteIds *)0)->ids)/sizeof(uint64_t)]; + TableRetain * retain; int32_t count; int lost; +} TableRetainIds; +typedef struct TableRetainWalk {TableRetain * retain;TableRetainIds * ids;TableRetainPath path;} TableRetainWalk; +static const uint64_t table_retain_known[]={UINT64_C(0x0a8f12cc5f9a0c03),UINT64_C(0x1e4984ef2e958a4c),UINT64_C(0x24b070ada2041cb0),UINT64_C(0x24f3a319b88552c1),UINT64_C(0x2f2ec0474f1c4fe4),UINT64_C(0x327fe6dc702553fd),UINT64_C(0x39f7fcec8fcb623d),UINT64_C(0x3bf8fbbad1587cdd),UINT64_C(0x4320e9a2e32eac38),UINT64_C(0x4339ee8ab21c8380),UINT64_C(0x4554e34a747022df),UINT64_C(0x4a9a31623ab5f213),UINT64_C(0x509220bb65a646b7),UINT64_C(0x56d7ab194448a4f3),UINT64_C(0x5b25b8ef511eb395),UINT64_C(0x60839e2395be697e),UINT64_C(0x69ff34904242a73d),UINT64_C(0x6dadeaaee49d6d18),UINT64_C(0x704be0d8faaffc58),UINT64_C(0x732dfbcc9b0cf0bb),UINT64_C(0x75d8e97600b296ea),UINT64_C(0x76aaaa535714d805),UINT64_C(0x77af761956600b54),UINT64_C(0x7a8060916400fe66),UINT64_C(0x7ce4fd9430e80cea),UINT64_C(0x7e9d0b96d39e517d),UINT64_C(0x802517e298c70b03),UINT64_C(0x823b8a195ce2133c),UINT64_C(0x9d167ef77aed79b6),UINT64_C(0x9d8b8aa2b404c2c8),UINT64_C(0x9deeefd89ca8a81d),UINT64_C(0xae3b9113b7db93a4),UINT64_C(0xaf63da4c8601e926),UINT64_C(0xaf63df4c8601f1a5),UINT64_C(0xaf63ef4c86020cd5),UINT64_C(0xb97e90a3784c431d),UINT64_C(0xbb62c62c9808ea37),UINT64_C(0xc4bcadba8e631b86),UINT64_C(0xd6458a3eef83d457),UINT64_C(0xd858c2cb7f1514cc),UINT64_C(0xdd9eb981e152e90c),UINT64_C(0xe5316cbaa025f028),UINT64_C(0xeddcb72b15486e77),UINT64_C(0xee5f6d7b48b44de8),UINT64_C(0xee7ba9ad45c64144),UINT64_C(0xf60ec899a5a69fa9),UINT64_C(0xffffffffffffffff),}; +static SCHEMA_UNUSED uint32_t table_retain_u32(const uint8_t * p) +{return (uint32_t)p[0]|(uint32_t)p[1]<<8|(uint32_t)p[2]<<16|(uint32_t)p[3]<<24; +} +static SCHEMA_UNUSED uint64_t table_retain_u64(const uint8_t * p) +{return table_retain_u32(p)|(uint64_t)table_retain_u32(p+4)<<32; +} +static SCHEMA_UNUSED void table_retain_put32(uint8_t * p,uint32_t v) +{int i; +for(i=0;i<4;i++)p[i]=(uint8_t)(v>>(8*i)); +} +static SCHEMA_UNUSED void table_retain_put64(uint8_t * p,uint64_t v) +{table_retain_put32(p,(uint32_t)v); +table_retain_put32(p+4,(uint32_t)(v>>32)); +} +static SCHEMA_UNUSED int64_t table_retain_leb_bytes(uint64_t v) +{int64_t n=1; +while(v>=128){v>>=7; +n++; +}return n; +} +static SCHEMA_UNUSED int table_retain_reserved(uint64_t id) +{return id>=UINT64_MAX-2; +} +static SCHEMA_UNUSED TableRetainPath table_retain_root(const void * at,uint32_t node) +{TableRetainPath p; +memset(&p,0,sizeof(p)); +p.at=at; +p.node=node; +return p; +} +static SCHEMA_UNUSED TableRetainWalk table_retain_step(TableRetainWalk keep,uint32_t ordinal,uint32_t index) +{if(keep.path.depth0&&keep.path.depth<=SCHEMA_TABLE_RETAIN_DEPTH_MAX)keep.path.steps[keep.path.depth-1].index=index; +return keep; +} +static SCHEMA_UNUSED uint8_t * table_retain_payload(uint8_t * record) +{return record+SCHEMA_TABLE_RETAIN_HEADER+(int64_t)table_retain_u32(record+8)*8; +} +static SCHEMA_UNUSED const void * table_retain_at(const TableRetain * retain,const uint8_t * record) +{uint32_t node=table_retain_u32(record+4); +if(!retain->directory||!node||node>retain->directory_count)return NULL; +return retain->base+retain->directory[node-1].offset; +} +static SCHEMA_UNUSED int table_retain_under(const TableRetain * retain,const uint8_t * record,const TableRetainPath * path,int field,uint32_t ordinal) +{ + uint32_t depth=table_retain_u32(record+8); + int32_t i; + const uint8_t * steps=record+SCHEMA_TABLE_RETAIN_HEADER; + if(path->depth<0||path->depth>SCHEMA_TABLE_RETAIN_DEPTH_MAX||table_retain_at(retain,record)!=path->at)return 0; + if(depth<(uint32_t)path->depth||(field&&depth==(uint32_t)path->depth))return 0; + for(i=0;idepth;i++)if(table_retain_u32(steps+8*i)!=path->steps[i].ordinal||table_retain_u32(steps+8*i+4)!=path->steps[i].index)return 0; + return !field||table_retain_u32(steps+8*path->depth)==ordinal; +} +static SCHEMA_UNUSED int table_retain_here(const TableRetain * retain,const uint8_t * record,const TableRetainPath * path) +{return table_retain_u32(record+8)==(uint32_t)path->depth&&table_retain_under(retain,record,path,0,0); +} +static SCHEMA_UNUSED void table_retain_discard(TableRetainWalk keep,int field,uint32_t ordinal) +{ + TableRetain * r=keep.retain; + int64_t read=0,write=0; + int32_t i,kept=0; + if(!r||!r->bytes)return; + for(i=0;icount;i++){uint32_t n=table_retain_u32(r->bytes+read); + if(!table_retain_under(r,r->bytes+read,&keep.path,field,ordinal)){if(read!=write)memmove(r->bytes+write,r->bytes+read,n); + write+=n; + kept++; + }read+=n; + }r->used=write; + r->count=kept; +} +static SCHEMA_UNUSED void table_retain_reset(TableRetain * retain,const TableNodeMap * nodes,const uint8_t * region) +{if(retain){retain->used=0; +retain->id_used=0; +retain->count=0; +retain->base=region; +retain->directory=nodes?nodes->entries:NULL; +retain->directory_count=nodes?(int64_t)nodes->count:0; +}} +static SCHEMA_UNUSED int table_retain_nameable(uint64_t id) +{int64_t lo=0,hi=(int64_t)(sizeof(table_retain_known)/sizeof(table_retain_known[0]))-1; +while(lo<=hi){int64_t mid=lo+(hi-lo)/2; +if(table_retain_known[mid]==id)return 1; +if(table_retain_known[mid]retain; + TableWriteIds * v=&ids->known; + if(record&&!table_retain_nameable(id)){ + if(!r){ids->lost=1; + return 0; + }for(i=0;iid_used;i++)if(r->ids[i].id==id)return (uint64_t)r->ids[i].slot; + if(!r->ids||r->id_used>=r->id_capacity||ids->count==INT32_MAX){ids->lost=1; + return 0; + } + r->ids[r->id_used].id=id; + r->ids[r->id_used++].slot=++ids->count; + return (uint64_t)ids->count; + } + for(i=0;icount;i++)if(v->ids[i]==id)return (uint64_t)ids->slots[i]; + if(v->count==(int)(sizeof(v->ids)/sizeof(v->ids[0]))||ids->count==INT32_MAX){*overflow=1; + return 0; + } + v->ids[v->count]=id; + ids->slots[v->count++]=++ids->count; + return (uint64_t)ids->count; +} +static SCHEMA_UNUSED void table_retain_truncate(TableRetainIds * ids,int32_t mark) +{while(ids->known.count&&ids->slots[ids->known.count-1]>mark)ids->known.count--; +if(ids->retain)while(ids->retain->id_used&&ids->retain->ids[ids->retain->id_used-1].slot>mark)ids->retain->id_used--; +ids->count=mark; +} +static SCHEMA_UNUSED void table_retain_writer_id(TableWriter * w,uint64_t id,TableRetainWalk keep) +{table_writer_leb(w,table_retain_ref(keep.ids,id,0,&w->overflow)); +} +static SCHEMA_UNUSED TableWriter table_retain_probe(const TableWriter * w,TableRetainWalk keep) +{TableWriter p=*w; +p.buffer=NULL; +p.capacity=INT64_MAX; +p.offset=0; +p.overflow=0; +p.id_checkpoint=keep.ids->count; +return p; +} +static SCHEMA_UNUSED void table_retain_rewind(const TableWriter * probe,TableRetainWalk keep) +{table_retain_truncate(keep.ids,probe->id_checkpoint); +} +static SCHEMA_UNUSED void table_retain_finish(TableWriter * w,TableRetainIds * ids) +{int32_t i=0,j=0; +TableRetain * r=ids->retain; +while(iknown.count||(r&&jid_used)){if(!r||j>=r->id_used||(iknown.count&&ids->slots[i]ids[j].slot))table_writer_put64(w,ids->known.ids[i++]); +else table_writer_put64(w,r->ids[j++].id); +}table_writer_put64(w,(uint64_t)ids->count); +} + +/* Capture writes only within the remaining record capacity. Failure leaves + used/count unchanged; a huge zero-bit message count reaches this bound + before it can command an unbounded transcode. */ +typedef struct TableRetainIn { + TableReader input;uint8_t * out;int64_t capacity,used;int failed; +} TableRetainIn; +static SCHEMA_UNUSED int table_retain_raw(TableRetainIn * s,const void * data,int64_t bytes) +{if(s->failed||bytes<0||s->used>s->capacity||bytes>s->capacity-s->used){s->failed=1; +return 0; +}if(bytes)memcpy(s->out+s->used,data,(size_t)bytes); +s->used+=bytes; +return 1; +} +static SCHEMA_UNUSED int table_retain_leb(TableRetainIn * s,uint64_t v) +{uint8_t b[10]; +int n=0; +do{b[n++]=(uint8_t)((v&127)|(v>=128?128:0)); +v>>=7; +}while(v); +return table_retain_raw(s,b,n); +} +static SCHEMA_UNUSED int table_retain_id(TableRetainIn * s,uint64_t id) +{uint8_t b[8]; +table_retain_put64(b,id); +return table_retain_raw(s,b,8); +} +static SCHEMA_UNUSED int table_retain_in_ref(TableRetainIn * s,int zero) +{uint64_t ref,id; +if(!table_reader_leb(&s->input,&ref))return 0; +if(!ref)return zero&&table_retain_id(s,0); +if(ref>s->input.id_count)return 0; +id=table_reader_id_at(&s->input,ref); +return id&&!table_retain_reserved(id)&&table_retain_id(s,id); +} +static SCHEMA_UNUSED int table_retain_in_payload(TableRetainIn *,uint8_t,int32_t); +static SCHEMA_UNUSED int table_retain_in_content(TableRetainIn *,uint8_t,int64_t,int32_t); +static SCHEMA_UNUSED int table_retain_in_frame(TableRetainIn * s,uint8_t kind,int64_t length,int32_t depth) +{uint8_t zero[8]={0}; +int64_t mark=s->used; +if(!table_retain_raw(s,zero,8)||!table_retain_in_content(s,kind,length,depth))return 0; +if(s->used-mark-8>UINT32_MAX)return 0; +table_retain_put32(s->out+mark,(uint32_t)(s->used-mark-8)); +return 1; +} +static SCHEMA_UNUSED int table_retain_in_content(TableRetainIn * s,uint8_t kind,int64_t length,int32_t depth) +{ + int64_t end; + TableReader * r=&s->input; + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX||!table_reader_has(r,length))return 0; + end=r->offset+length; + switch(kind){ + case 13:for(;;){uint64_t ref; + int64_t mark=r->offset; + uint8_t k; + if(!table_reader_leb(r,&ref))return 0; + if(!ref){if(!table_retain_id(s,0))return 0; + break; + }r->offset=mark; + if(!table_retain_in_ref(s,0)||r->offset>=end)return 0; + k=table_reader_get8(r); + if(!table_retain_raw(s,&k,1)||!table_retain_in_payload(s,k,depth)||r->offset>end)return 0; + }break; + case 14:case 16:{uint8_t elem; + uint64_t n,i; + if(r->offset>=end)return 0; + elem=table_reader_get8(r); + if(!table_retain_raw(s,&elem,1)||!table_reader_leb(r,&n)||!table_retain_leb(s,n))return 0; + for(i=0;i(uint64_t)(end-r->offset)||!table_retain_in_frame(s,elem,(int64_t)bytes,depth+1))return 0; + }else if(!table_retain_in_payload(s,elem,depth))return 0; + if(r->offset>end)return 0; + }break; + } + case 15:case 30:if(!table_retain_in_payload(s,kind,depth))return 0; + break; + case 17:return 0; + default:if(!table_retain_raw(s,r->buffer+r->offset,length))return 0; + r->offset+=length; + break; + }return r->offset==end; +} +static SCHEMA_UNUSED int table_retain_width(uint8_t kind) +{switch(kind){case 1:case 2:case 6:case 20:case 25:return 1; +case 3:case 7:case 21:case 26:return 2; +case 4:case 8:case 10:case 22:case 27:return 4; +case 5:case 9:case 11:case 23:case 28:return 8; +case 18:case 19:case 24:case 29:return 16; +default:return 0; +}} +static SCHEMA_UNUSED int table_retain_in_payload(TableRetainIn * s,uint8_t kind,int32_t depth) +{ + TableReader * r=&s->input; + uint64_t length; + int width=table_retain_width(kind); + if(width){if(!table_reader_has(r,width)||!table_retain_raw(s,r->buffer+r->offset,width))return 0; + r->offset+=width; + return 1; + } + switch(kind){ + case 12:case 31:case 32:case 33:if(!table_reader_leb(r,&length)||!table_reader_room(r,length)||!table_retain_leb(s,length)||!table_retain_raw(s,r->buffer+r->offset,(int64_t)length))return 0; + r->offset+=(int64_t)length; + return 1; + case 13:case 14:case 16:if(!table_reader_leb(r,&length)||!table_reader_room(r,length))return 0; + return table_retain_in_frame(s,kind,(int64_t)length,depth+1); + case 15:{uint64_t arm; + uint8_t k; + int64_t mark=r->offset; + if(!table_reader_leb(r,&arm))return 0; + r->offset=mark; + if(!table_retain_in_ref(s,1))return 0; + if(!arm)return 1; + if(!table_reader_has(r,1))return 0; + k=table_reader_get8(r); + if(!table_retain_raw(s,&k,1)||!table_reader_leb(r,&length)||!table_reader_room(r,length))return 0; + return table_retain_in_frame(s,k,(int64_t)length,depth+1); + } + case 30:return table_retain_in_ref(s,1); + default:return 0; + } +} +static SCHEMA_UNUSED uint8_t * table_retain_begin(TableRetainWalk keep,uint64_t id,uint8_t kind,TableRetainIn * s) +{ + TableRetain * r=keep.retain; + int64_t header=SCHEMA_TABLE_RETAIN_HEADER+8*(int64_t)keep.path.depth; + uint8_t * p; + int32_t i; + if(table_retain_reserved(id)||!r||!r->bytes||keep.path.depth<0||keep.path.depth>SCHEMA_TABLE_RETAIN_DEPTH_MAX||r->used<0||r->used>r->capacity||header>r->capacity-r->used||r->count==INT32_MAX)return NULL; + p=r->bytes+r->used; + table_retain_put32(p+4,keep.path.node); + table_retain_put32(p+8,(uint32_t)keep.path.depth); + table_retain_put64(p+16,id); + p[24]=kind; + p[25]=0; + for(i=0;iout=p+header; + s->capacity=r->capacity-r->used-header; + if(s->capacity>UINT32_MAX-header)s->capacity=UINT32_MAX-header; + return p; +} +static SCHEMA_UNUSED void table_retain_commit(TableRetainWalk keep,uint8_t * record,const TableRetainIn * s,TableReport * report) +{int64_t bytes=SCHEMA_TABLE_RETAIN_HEADER+8*(int64_t)keep.path.depth+s->used; +table_retain_put32(record,(uint32_t)bytes); +table_retain_put32(record+12,(uint32_t)s->used); +keep.retain->used+=bytes; +keep.retain->count++; +report->retained++; +} +static SCHEMA_UNUSED int table_retain_capture(TableReader * r,uint64_t id,uint8_t kind,TableRetainWalk keep) +{ + TableRetainIn s; + TableReader source=*r; + uint8_t * record; + if(!table_reader_skip(r,kind))return 0; + if(!keep.retain)return 1; + record=table_retain_begin(keep,id,kind,&s); + if(!record){r->report->retain_lost++; + return 1; + }source.size=r->offset; + s.input=source; + if(!table_retain_in_payload(&s,kind,0)||s.input.offset!=source.size){r->report->retain_lost++; + return 1; + }table_retain_commit(keep,record,&s,r->report); + return 1; +} + + +typedef struct TableRetainOut { + uint8_t * input;int64_t size,at,bytes;TableRetainIds * ids;TableWriter * writer;int overflow; +} TableRetainOut; +static SCHEMA_UNUSED int table_retain_out_raw(TableRetainOut * s,const void * data,int64_t bytes) +{if(bytes<0||bytes>INT64_MAX-s->bytes)return 0; +if(s->writer)table_writer_raw(s->writer,data,bytes); +s->bytes+=bytes; +return !s->writer||!s->writer->overflow; +} +static SCHEMA_UNUSED int table_retain_out_leb(TableRetainOut * s,uint64_t v) +{if(s->writer)table_writer_leb(s->writer,v); +s->bytes+=table_retain_leb_bytes(v); +return !s->writer||!s->writer->overflow; +} +static SCHEMA_UNUSED int table_retain_out_leb_read(TableRetainOut * s,uint64_t * v) +{TableReader r=table_reader_make(s->input,s->size,NULL); +r.offset=s->at; +if(!table_reader_leb(&r,v))return 0; +s->at=r.offset; +return 1; +} +static SCHEMA_UNUSED int table_retain_out_ref(TableRetainOut * s) +{uint64_t id,ref; +if(s->size-s->at<8)return 0; +id=table_retain_u64(s->input+s->at); +s->at+=8; +if(!id)return table_retain_out_leb(s,0); +ref=table_retain_ref(s->ids,id,1,&s->overflow); +return !s->ids->lost&&!s->overflow&&table_retain_out_leb(s,ref); +} +static SCHEMA_UNUSED int table_retain_out_payload(TableRetainOut *,uint8_t,int32_t); +static SCHEMA_UNUSED int table_retain_out_content(TableRetainOut *,uint8_t,int64_t,int32_t); +static SCHEMA_UNUSED int table_retain_out_frame(TableRetainOut * s,uint8_t kind,int32_t depth) +{ + uint8_t * slot; + int64_t resolved,wire,begin; + if(s->size-s->at<8)return 0; + slot=s->input+s->at; + resolved=table_retain_u32(slot); + s->at+=8; + if(!s->writer){begin=s->bytes; + if(!table_retain_out_content(s,kind,resolved,depth))return 0; + wire=s->bytes-begin; + if(wire>UINT32_MAX)return 0; + table_retain_put32(slot+4,(uint32_t)wire); + s->bytes+=table_retain_leb_bytes((uint64_t)wire); + return 1; + } + wire=table_retain_u32(slot+4); + if(!table_retain_out_leb(s,(uint64_t)wire))return 0; + begin=s->bytes; + return table_retain_out_content(s,kind,resolved,depth)&&s->bytes-begin==wire; +} +static SCHEMA_UNUSED int table_retain_out_content(TableRetainOut * s,uint8_t kind,int64_t length,int32_t depth) +{ + int64_t end; + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX||length<0||length>s->size-s->at)return 0; + end=s->at+length; + switch(kind){ + case 13:for(;;){uint64_t id; + uint8_t k; + if(end-s->at<8)return 0; + id=table_retain_u64(s->input+s->at); + if(!table_retain_out_ref(s))return 0; + if(!id)break; + if(s->at>=end)return 0; + k=s->input[s->at++]; + if(!table_retain_out_raw(s,&k,1)||!table_retain_out_payload(s,k,depth))return 0; + }break; + case 14:case 16:{uint64_t n,i; + uint8_t elem; + if(s->at>=end)return 0; + elem=s->input[s->at++]; + if(!table_retain_out_raw(s,&elem,1)||!table_retain_out_leb_read(s,&n)||!table_retain_out_leb(s,n))return 0; + for(i=0;iinput+s->at,length))return 0; + s->at+=length; + break; + }return s->at==end; +} +static SCHEMA_UNUSED int table_retain_out_payload(TableRetainOut * s,uint8_t kind,int32_t depth) +{ + int width=table_retain_width(kind); + uint64_t length; + if(width){if(width>s->size-s->at||!table_retain_out_raw(s,s->input+s->at,width))return 0; + s->at+=width; + return 1; + } + switch(kind){ + case 12:case 31:case 32:case 33:if(!table_retain_out_leb_read(s,&length)||length>(uint64_t)(s->size-s->at)||!table_retain_out_leb(s,length)||!table_retain_out_raw(s,s->input+s->at,(int64_t)length))return 0; + s->at+=(int64_t)length; + return 1; + case 13:case 14:case 16:return table_retain_out_frame(s,kind,depth+1); + case 15:{uint64_t arm; + uint8_t k; + if(s->size-s->at<8)return 0; + arm=table_retain_u64(s->input+s->at); + if(!table_retain_out_ref(s))return 0; + if(!arm)return 1; + if(s->at>=s->size)return 0; + k=s->input[s->at++]; + return table_retain_out_raw(s,&k,1)&&table_retain_out_frame(s,k,depth+1); + } + case 30:return table_retain_out_ref(s); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t table_retain_record_measure(uint8_t * record,TableRetainIds * ids,uint64_t * ref) +{ + int32_t mark=ids->count; + TableRetainOut s; + int overflow=0; + ids->lost=0; + *ref=table_retain_ref(ids,table_retain_u64(record+16),1,&overflow); + if(!ids->lost&&!overflow){memset(&s,0,sizeof(s)); + s.input=table_retain_payload(record); + s.size=table_retain_u32(record+12); + s.ids=ids; + if(table_retain_out_payload(&s,record[24],0)&&s.at==s.size)return table_retain_leb_bytes(*ref)+1+s.bytes; + } + table_retain_truncate(ids,mark); + ids->lost=0; + return -1; +} +static SCHEMA_UNUSED int table_retain_tail(TableWriter * w,TableRetainWalk keep) +{ + TableRetain * r=keep.retain; + int32_t i; + int64_t at=0; + if(!r||!r->bytes)return 1; + for(i=0;icount;i++){uint8_t * record=r->bytes+at; + uint64_t ref; + int64_t bytes; + TableRetainOut s; + at+=table_retain_u32(record); + if(!table_retain_here(r,record,&keep.path))continue; + if(w->check_default){w->offset=2; + return 1; + } + bytes=table_retain_record_measure(record,keep.ids,&ref); + if(bytes<0)continue; + if(!w->buffer){if(bytes>INT64_MAX-w->offset){w->overflow=1; + return 0; + }w->offset+=bytes; + continue; + } + table_writer_leb(w,ref); + table_writer_put8(w,record[24]); + memset(&s,0,sizeof(s)); + s.input=table_retain_payload(record); + s.size=table_retain_u32(record+12); + s.ids=keep.ids; + s.writer=w; + if(!table_retain_out_payload(&s,record[24],0)||s.at!=s.size)return 0; + record[25]=1; + }return !w->overflow; +} +static SCHEMA_UNUSED void table_retain_clear_placed(TableRetain * r) +{int32_t i; +int64_t at=0; +if(!r||!r->bytes)return; +for(i=0;icount;i++){r->bytes[at+25]=0; +at+=table_retain_u32(r->bytes+at); +}} +static SCHEMA_UNUSED void table_retain_count_lost(TableRetain * r,TableReport * report) +{int32_t i; +int64_t at=0; +if(!r||!r->bytes)return; +for(i=0;icount;i++){if(!r->bytes[at+25])report->retain_lost++; +at+=table_retain_u32(r->bytes+at); +}} + + +typedef struct TableRetainMessage {TableRetainIn output;TableMessageReader input;} TableRetainMessage; +static SCHEMA_UNUSED int table_retain_message_payload(TableRetainMessage *,const TableMessageEntry *,int32_t); +static SCHEMA_UNUSED int table_retain_message_content(TableRetainMessage *,const TableMessageEntry *,int32_t); +static SCHEMA_UNUSED int table_retain_message_frame(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{uint8_t zero[8]={0}; +int64_t mark=s->output.used; +if(!table_retain_raw(&s->output,zero,8)||!table_retain_message_content(s,e,depth))return 0; +if(s->output.used-mark-8>UINT32_MAX)return 0; +table_retain_put32(s->output.out+mark,(uint32_t)(s->output.used-mark-8)); +return 1; +} +static SCHEMA_UNUSED int table_retain_message_scalar(TableRetainMessage * s,const TableMessageEntry * e) +{ + uint64_t lo=0,hi=0; + uint8_t bytes[16]; + int width=table_retain_width(e->kind); + if(!width)return 0; + if(e->kind==10&&e->packing==2){float value; + if(!table_message_float(&s->input,e,&value))return 0; + lo=table_float_to_bits(value); + } + else if(!table_message_number(&s->input,e,&lo,&hi))return 0; + table_retain_put64(bytes,lo); + table_retain_put64(bytes+8,hi); + return table_retain_raw(&s->output,bytes,width); +} +static SCHEMA_UNUSED int table_retain_message_opaque(TableRetainMessage * s,const TableMessageEntry * e,int framed) +{ + uint64_t n,i; + TableMessageReader * r=&s->input; + if(e->kind==12||e->kind==33){if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==33)n*=2; + else if(!table_bit_align_read(&r->bits))return 0; + } + else if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + if(n>INT64_MAX/8||!table_bit_has(&r->bits,(int64_t)n*8))return 0; + if(framed&&!table_retain_leb(&s->output,n))return 0; + if(n>(uint64_t)(s->output.capacity-s->output.used))return 0; + for(i=0;ibits,&v,8))return 0; + byte=(uint8_t)v; + if(!table_retain_raw(&s->output,&byte,1))return 0; + }return 1; +} +static SCHEMA_UNUSED int table_retain_message_content(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{ + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX)return 0; + switch(e->kind){ + case 13:for(;;){const TableMessageEntry * f; + if(!table_message_ref(&s->input,&f,0))return 0; + if(!f)return table_retain_id(&s->output,0); + if(!f->id||!table_retain_id(&s->output,f->id)||!table_retain_raw(&s->output,&f->kind,1)||!table_retain_message_payload(s,f,depth))return 0; + } + case 14:case 16:{uint64_t n,i; + TableMessageEntry elem=table_message_element(e); + if(elem.kind==17||!table_message_count(&s->input,e,&n)||!table_retain_raw(&s->output,&elem.kind,1)||!table_retain_leb(&s->output,n))return 0; + for(i=0;ikind==16){const TableMessageEntry * key; + if(!table_message_ref(&s->input,&key,1)||!key||!key->id||!table_retain_id(&s->output,key->id)||!table_retain_message_frame(s,&elem,depth+1))return 0; + }else if(!table_retain_message_payload(s,&elem,depth))return 0; + }return 1; + } + case 15:case 30:return table_retain_message_payload(s,e,depth); + case 32:return 1; + case 12:case 31:case 33:return table_retain_message_opaque(s,e,0); + case 0:case 17:return 0; + default:return table_retain_message_scalar(s,e); + } +} +static SCHEMA_UNUSED int table_retain_message_payload(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{ + switch(e->kind){ + case 0:case 17:return 0; + case 32:return table_retain_leb(&s->output,0); + case 30:{const TableMessageEntry * entry; + if(!table_message_ref(&s->input,&entry,1))return 0; + return table_retain_id(&s->output,entry?entry->id:0); + } + case 15:{const TableMessageEntry * arm; + if(!table_message_ref(&s->input,&arm,0))return 0; + if(!arm)return table_retain_id(&s->output,0); + return arm->id&&arm->kind&&table_retain_id(&s->output,arm->id)&&table_retain_raw(&s->output,&arm->kind,1)&&table_retain_message_frame(s,arm,depth+1); + } + case 13:case 14:case 16:return table_retain_message_frame(s,e,depth+1); + case 12:case 31:case 33:return table_retain_message_opaque(s,e,1); + default:return table_retain_message_scalar(s,e); + } +} +static SCHEMA_UNUSED int table_retain_message_capture(TableMessageReader * r,const TableMessageEntry * entry,TableRetainWalk keep) +{ + TableRetainMessage s; + uint8_t * record; + s.input=*r; + if(!table_message_skip(r,entry))return 0; + if(!keep.retain)return 1; + record=table_retain_begin(keep,entry->id,entry->kind,&s.output); + if(!record||!table_retain_message_payload(&s,entry,0)||s.input.bits.offset!=r->bits.offset){r->report->retain_lost++; + return 1; + } + table_retain_commit(keep,record,&s.output,r->report); + return 1; +} + +typedef int (*TableRetainSaveNode)(TableWriter *,const void *,uint64_t,TableRetainWalk); +static SCHEMA_UNUSED int table_retain_records(TableWriter * w,TableRetainWalk retention,TableRetainSaveNode save) +{ + uint64_t i; + const TableNumbering * n=w->nodes; + table_writer_leb(w,n->count-1); + for(i=1;icount;i++){const TableNodeEntry * e=n->entries+i; + retention.path=table_retain_root(e->value,0); + table_retain_writer_id(w,e->type->id,retention); + if(!w->buffer){int64_t begin=w->offset; + if(!save(w,e->value,e->type->id,retention))return 0; + table_writer_leb(w,(uint64_t)(w->offset-begin)); + } + else {TableWriter probe=table_retain_probe(w,retention); + if(!save(&probe,e->value,e->type->id,retention))return 0; + table_retain_rewind(&probe,retention); + table_writer_leb(w,(uint64_t)probe.offset); + if(!save(w,e->value,e->type->id,retention))return 0; + }} + return !w->overflow; +} +static SCHEMA_UNUSED int64_t table_retain_graph_save(const void * root,const TableNodeType * type,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator,TableRetainSaveNode save) +{ + TableNumbering numbering; + TableRetainIds ids; + TableRetainWalk retention; + TableWriter w; + int64_t result=-1; + memset(&ids,0,sizeof(ids)); + ids.retain=retain; + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.ids=&ids; + retention.path=table_retain_root(root,1); + if(retain)retain->id_used=0; + if(buffer)table_retain_clear_placed(retain); + w=table_writer_make(buffer,capacity,&ids.known); + if(!table_number_build(&numbering,NULL,root,type,allocator)){table_number_dispose(&numbering); + return -1; + }w.nodes=&numbering; + table_writer_put8(&w,1); + if(!save(&w,root,type->id,retention))goto done; + w.offset--; + if(numbering.count>1){table_retain_writer_id(&w,UINT64_MAX,retention); + table_writer_put8(&w,12); + if(!buffer){int64_t begin=w.offset; + if(!table_retain_records(&w,retention,save))goto done; + table_writer_leb(&w,(uint64_t)(w.offset-begin)); + } + else {TableWriter probe=table_retain_probe(&w,retention); + if(!table_retain_records(&probe,retention,save))goto done; + table_retain_rewind(&probe,retention); + table_writer_leb(&w,(uint64_t)probe.offset); + if(!table_retain_records(&w,retention,save))goto done; + }} + table_writer_put8(&w,0); + table_retain_finish(&w,&ids); + if(!w.overflow){result=w.offset; + if(buffer)table_retain_count_lost(retain,report); + } + done:table_number_dispose(&numbering); + return result; +} + +#endif + +/* table Tally — TABLE-wire storage: relocatable, bounded. C has no member + initializers, so the declared defaults live in tally_reset and nowhere + else — one definition of what a default is (docs/SPEC-TABLES.md) */ +typedef struct Tally { + int32_t hits; +} Tally; + +/* table Marker — TABLE-wire storage: relocatable, bounded. C has no member + initializers, so the declared defaults live in marker_reset and nowhere + else — one definition of what a default is (docs/SPEC-TABLES.md) */ +typedef struct Marker { + char label[8 + 1]; /* string(8): N + 1 for the terminator the wire does not carry */ + int32_t label_length; + TableRef note; /* *Tally — null until assigned */ +} Marker; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void tally_reset( Tally * value ); +static SCHEMA_UNUSED void schema_graphdemo_tally_reset_raw_( void * storage ); +static SCHEMA_UNUSED void marker_reset( Marker * value ); +static SCHEMA_UNUSED void schema_graphdemo_marker_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void tally_reset( Tally * value ) +{ + value->hits = 0; } static SCHEMA_UNUSED void schema_graphdemo_tally_reset_raw_( void * storage ) { tally_reset( (Tally *) storage ); } @@ -1026,350 +3068,1006 @@ static SCHEMA_UNUSED Marker * marker_emplace( TableSink * sink, TableRef * slot slot->value = (int64_t) at; return node; } - return NULL; + return NULL; +} + +/* ---- codecs: measure/save/load per closure member ---- */ + +static SCHEMA_UNUSED int64_t tally_measure( const Tally * value ); +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int tally_save_body( TableWriter * w, const Tally * value ); +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int tally_load_body( TableReader * r, Tally * value ); +static SCHEMA_UNUSED int marker_save_body( TableWriter * w, const Marker * value ); +static SCHEMA_UNUSED int marker_load_body( TableReader * r, Marker * value ); + +static SCHEMA_UNUSED int schema_graphdemo_tally_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_marker_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_tally_arena_alignment_, SCHEMA_TABLE_ALIGNOF(Tally) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_tally_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_tally_graph_edges_( TableNumbering * numbering, const void * storage ); +SCHEMA_TABLE_STATIC_ASSERT( schema_graphdemo_marker_arena_alignment_, SCHEMA_TABLE_ALIGNOF(Marker) <= kTableAlign, "a table node's alignment must fit the arena's" ); +static const TableNodeType schema_graphdemo_marker_node_type_; +static SCHEMA_UNUSED int schema_graphdemo_marker_graph_edges_( TableNumbering * numbering, const void * storage ); +static SCHEMA_UNUSED int schema_graphdemo_tally_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_tally_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_tally_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int schema_graphdemo_marker_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at); +static SCHEMA_UNUSED int schema_graphdemo_marker_wire_extent_(TableReader r,int64_t * at); +static SCHEMA_UNUSED int64_t schema_graphdemo_marker_wire_storage_(const TableReader * r); +static SCHEMA_UNUSED int tally_save_message_body(TableBitWriter *,const Tally *); +static SCHEMA_UNUSED int marker_save_message_body(TableBitWriter *,const Marker *); +static SCHEMA_UNUSED int tally_load_message_body(TableMessageReader *,Tally *); +static SCHEMA_UNUSED int marker_load_message_body(TableMessageReader *,Marker *); +static SCHEMA_UNUSED int schema_graphdemo_tally_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_marker_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int tally_save_body( TableWriter * w, const Tally * value ) +{ + (void) value; + { /* hits */ + if ( !( value->hits == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x732dfbcc9b0cf0bbull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->hits ) ); + } + } + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int64_t tally_measure( const Tally * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !tally_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + +static SCHEMA_UNUSED int64_t tally_save( const Tally * value, uint8_t * buffer, int64_t capacity ) +{ + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !tally_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + +static SCHEMA_UNUSED int tally_load_body( TableReader * r, Tally * value ) +{ + tally_reset( value ); + for ( ;; ) + { + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) + { + case 0x732dfbcc9b0cf0bbull: /* hits */ + { + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 10000ll ) { decoded_wide = 10000ll; r->report->clamped++; } + value->hits = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 10000ll ) { decoded_wide = 10000ll; r->report->clamped++; } + value->hits = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 10000 ) { decoded_v = 10000; r->report->clamped++; } + value->hits = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 10000ll ) { decoded_wide = 10000ll; r->report->clamped++; } + value->hits = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 10000ll ) { decoded_wide = 10000ll; r->report->clamped++; } + value->hits = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 10000 ) { decoded_v = 10000; r->report->clamped++; } + value->hits = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + break; + } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; + default: + r->report->unknown++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + } +} + +static SCHEMA_UNUSED int tally_load( Tally * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + TableReport ignored; TableReader r; + memset( &ignored, 0, sizeof( ignored ) ); + if ( report == NULL ) { report = &ignored; } + tally_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } + return tally_load_body( &r, value ); +} + +static SCHEMA_UNUSED int tally_save_message_body(TableBitWriter * w,const Tally * value) +{ + (void)value; + { /* hits */ + if(!(value->hits == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,31,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->hits)-UINT64_C(0),14); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t tally_measure_messages(const Tally * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x732dfbcc9b0cf0bb): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>10000ll){decoded=10000ll; + r->report->clamped++; + } + value->hits=(int32_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int tally_load_messages(Tally * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int schema_graphdemo_marker_wire_label_( TableWriter * w, const Marker * value ) +{ + if ( value->label_length < 0 || value->label_length > 8 ) { return 0; } + table_writer_raw( w, value->label, value->label_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int marker_save_body( TableWriter * w, const Marker * value ) +{ + (void) value; + { /* label */ + if ( value->label_length < 0 || value->label_length > 8 ) { return 0; } + if ( value->label_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x39f7fcec8fcb623dull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_marker_wire_label_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_marker_wire_label_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_marker_wire_label_( w, value ) ) { return 0; } + } + } + } + { /* note */ + if ( !( value->note.value == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x3bf8fbbad1587cddull ); table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->note); uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; } table_writer_leb(w,index); } + } + } + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int marker_load_body( TableReader * r, Marker * value ) +{ + marker_reset( value ); + for ( ;; ) + { + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) + { + case 0x39f7fcec8fcb623dull: /* label */ + { + if ( kind != 12 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->label[0] = 0; value->label_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 8 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 8 ); r->report->clamped++; } + memcpy( value->label, sub.buffer, (size_t) keep ); value->label[keep] = 0; value->label_length = (int32_t) keep; + break; + } + case 0x3bf8fbbad1587cddull: /* note */ + { + if ( kind != 17 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + { uint64_t index; if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; return 0; } + table_node_resolve((*r).nodes,&value->note,index,UINT64_C(0x69ff34904242a73d),r->report); } + break; + } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; + default: + r->report->unknown++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + break; + } + } } -/* ---- codecs: measure/save/load per closure member ---- */ - -static SCHEMA_UNUSED int64_t tally_measure( const Tally * value ); -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int tally_save_body( TableWriter * w, const Tally * value ); -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int tally_load_body( TableReader * r, Tally * value ); -static SCHEMA_UNUSED int64_t marker_measure_body( const TableCtx * ctx, const Marker * value, int32_t depth ); -static SCHEMA_UNUSED int marker_save_body( const TableCtx * ctx, TableWriter * w, const Marker * value, int32_t depth ); -static SCHEMA_UNUSED int marker_load_body( TableReader * r, TableSink * sink, Marker * value, int32_t depth ); - -/* ---- pointer-graph walkers: pack (Lock), size (Load) ---- */ - -static SCHEMA_UNUSED int64_t tally_pack_measure( const TableCtx * ctx, const Tally * value, int32_t depth ); -static SCHEMA_UNUSED int tally_pack( const TableCtx * ctx, const Tally * src, Tally * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t tally_load_measure_body( TableReader * r, int32_t depth ); -static SCHEMA_UNUSED int64_t marker_pack_measure( const TableCtx * ctx, const Marker * value, int32_t depth ); -static SCHEMA_UNUSED int marker_pack( const TableCtx * ctx, const Marker * src, Marker * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ); -static SCHEMA_UNUSED int64_t marker_load_measure_body( TableReader * r, int32_t depth ); - -static SCHEMA_UNUSED int64_t tally_measure( const Tally * value ) +static SCHEMA_UNUSED int marker_save_message_body(TableBitWriter * w,const Marker * value) { - int64_t bytes = 2; /* terminator */ - if ( value->hits != 0 ) { bytes += 3 + 4; } /* hits */ - return bytes; + (void)value; + { /* label */ + if(value->label_length<0 || value->label_length>8) return 0; + if(value->label_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,16,kTableMessageRefBitsHere); + if(value->label_length<0 || value->label_length>8) return 0; + table_bit_put(w,(uint64_t)value->label_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->label,value->label_length); + } + } + { /* note */ + if(!(value->note.value == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,17,kTableMessageRefBitsHere); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->note); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) return 0; + table_bit_put(w,index,w->index_bits); + } + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int tally_save_body( TableWriter * w, const Tally * value ) +static SCHEMA_UNUSED int marker_load_message_body(TableMessageReader * r,Marker * value) { - if ( value->hits != 0 ) - { - table_writer_put16( w, 0xb723 ); table_writer_put8( w, 4 ); /* hits */ - table_writer_put32( w, (uint32_t) ( value->hits ) ); + marker_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x39f7fcec8fcb623d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,8); + memcpy(value->label,text,(size_t)kept); + value->label[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->label_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x3bf8fbbad1587cdd): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->note,index,UINT64_C(0x69ff34904242a73d),r->report); + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int schema_graphdemo_marker_message_extent_(TableMessageReader * r,int64_t * at) +{ + (void)at; + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + switch(entry->id){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int tally_save_body_retain(TableWriter *,const Tally *, TableRetainWalk retention); +static SCHEMA_UNUSED int tally_load_body_retain(TableReader *,Tally *, TableRetainWalk retention); +static SCHEMA_UNUSED int marker_save_body_retain(TableWriter *,const Marker *, TableRetainWalk retention); +static SCHEMA_UNUSED int marker_load_body_retain(TableReader *,Marker *, TableRetainWalk retention); +static SCHEMA_UNUSED int tally_load_message_body_retain(TableMessageReader *,Tally *, TableRetainWalk retention); +static SCHEMA_UNUSED int marker_load_message_body_retain(TableMessageReader *,Marker *, TableRetainWalk retention); +static SCHEMA_UNUSED int tally_save_body_retain( TableWriter * w, const Tally * value , TableRetainWalk retention) +{ + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* hits */ + retention=table_retain_step(body_keep,0,0); + if ( !( value->hits == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x732dfbcc9b0cf0bbull , retention); + table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->hits ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); return !w->overflow; } -static SCHEMA_UNUSED int64_t tally_save( const Tally * value, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int tally_load_body_retain( TableReader * r, Tally * value , TableRetainWalk retention) { - TableWriter w = table_writer_make( buffer, capacity ); - if ( !tally_save_body( &w, value ) ) { return -1; } - return w.offset; /* == tally_measure( value ) */ -} + (void)retention; -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int tally_load_body( TableReader * r, Tally * value ) -{ - tally_reset( value ); /* prefill declared defaults in place, then overlay */ + tally_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); for ( ;; ) { - uint16_t field_id; + uint64_t ref, id; uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); - switch ( field_id ) + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) { - case 0xb723: /* hits */ + case 0x732dfbcc9b0cf0bbull: /* hits */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 10000ll ) { decoded_wide = 10000ll; + r->report->clamped++; + } + value->hits = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 10000ll ) { decoded_wide = 10000ll; + r->report->clamped++; + } + value->hits = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 10000 ) { decoded_v = 10000; + r->report->clamped++; + } + value->hits = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + retention=table_retain_step(body_keep,0,0); + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 10000 ) { decoded_v = 10000; r->report->clamped++; } - value->hits = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 10000ll ) { decoded_wide = 10000ll; + r->report->clamped++; + } + value->hits = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 10000ll ) { decoded_wide = 10000ll; + r->report->clamped++; + } + value->hits = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 10000 ) { decoded_v = 10000; + r->report->clamped++; + } + value->hits = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; } break; } - default: - { - r->report->unknown++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } break; - } + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; } } } -static SCHEMA_UNUSED int tally_load( Tally * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) +static SCHEMA_UNUSED int tally_load_message_body_retain(TableMessageReader * r,Tally * value, TableRetainWalk retention) { - TableReport ignored; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); - return tally_load_body( &r, value ); + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + tally_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x732dfbcc9b0cf0bb): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>10000ll){decoded=10000ll; + r->report->clamped++; + } + value->hits=(int32_t)decoded; + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; } - -static SCHEMA_UNUSED int64_t marker_measure_body( const TableCtx * ctx, const Marker * value, int32_t depth ) +static SCHEMA_UNUSED int schema_graphdemo_marker_wire_label__retain( TableWriter * w, const Marker * value , TableRetainWalk retention) { - int64_t bytes = 2; /* terminator */ - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - if ( value->label_length < 0 || value->label_length > 8 ) { return -1; } /* storage invariant */ - if ( value->label_length > 0 ) { bytes += 3 + 4 + value->label_length; } /* label */ - { - const Tally * pointee_note = tally_at( ctx, &value->note ); /* *Tally */ - if ( pointee_note != NULL ) - { - int64_t body_note = tally_measure( pointee_note ); - if ( body_note < 0 ) { return -1; } - /* a pointer's PRESENCE is the payload: it rides even when the - pointee is all-default, or null and non-null would be one */ - bytes += 3 + 4 + body_note; - } + (void)retention; + + if ( value->label_length < 0 || value->label_length > 8 ) { return 0; } - return bytes; + table_writer_raw( w, value->label, value->label_length ); + return !w->overflow; } -static SCHEMA_UNUSED int marker_save_body( const TableCtx * ctx, TableWriter * w, const Marker * value, int32_t depth ) +static SCHEMA_UNUSED int marker_save_body_retain( TableWriter * w, const Marker * value , TableRetainWalk retention) { - if ( depth > kTableMaxDepth ) { return 0; } /* a data cycle, or a chain past the cap */ - if ( value->label_length < 0 || value->label_length > 8 ) { return 0; } /* storage invariant */ - if ( value->label_length > 0 ) - { - table_writer_put16( w, 0xe16a ); table_writer_put8( w, 12 ); /* label */ - table_writer_put32( w, (uint32_t) value->label_length ); - table_writer_raw( w, value->label, value->label_length ); + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* label */ + retention=table_retain_step(body_keep,0,0); + if ( value->label_length < 0 || value->label_length > 8 ) { return 0; + } + if ( value->label_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x39f7fcec8fcb623dull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_marker_wire_label__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_marker_wire_label__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_marker_wire_label__retain( w, value , retention) ) { return 0; + } + } + } } - { - const Tally * pointee_note = tally_at( ctx, &value->note ); /* *Tally */ - if ( pointee_note != NULL ) + { /* note */ + retention=table_retain_step(body_keep,1,0); + if ( !( value->note.value == 0 ) ) { - int64_t body_note = tally_measure( pointee_note ); - if ( body_note < 0 ) { return 0; } - table_writer_put16( w, 0x9da7 ); table_writer_put8( w, 13 ); /* note — the pointee rides as a nested body */ - table_writer_put32( w, (uint32_t) body_note ); - if ( !tally_save_body( w, pointee_note ) ) { return 0; } + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x3bf8fbbad1587cddull , retention); + table_writer_put8( w, 17 ); + { const void * node=table_ref_at(w->nodes ? w->nodes->ctx : NULL,&value->note); + uint64_t index=table_number_find(w->nodes,node); + if(node!=NULL && index==0) { return 0; + } table_writer_leb(w,index); + } } } - table_writer_put16( w, 0 ); /* terminator */ + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); return !w->overflow; } -static SCHEMA_UNUSED int marker_load_body( TableReader * r, TableSink * sink, Marker * value, int32_t depth ) +static SCHEMA_UNUSED int marker_load_body_retain( TableReader * r, Marker * value , TableRetainWalk retention) { - marker_reset( value ); /* prefill declared defaults in place, then overlay */ + (void)retention; + + marker_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); for ( ;; ) { - uint16_t field_id; + uint64_t ref, id; uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); - switch ( field_id ) + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) { - case 0xe16a: /* label */ + case 0x39f7fcec8fcb623dull: /* label */ { if ( kind != 12 ) { r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; + value->label[0] = 0; + value->label_length = 0; break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 8 ) { keep = 8; r->report->clamped++; } - memcpy( value->label, r->buffer + r->offset, keep ); + keep = (uint64_t) sub.size; + if ( keep > 8 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 8 ); + r->report->clamped++; + } + memcpy( value->label, sub.buffer, (size_t) keep ); value->label[keep] = 0; value->label_length = (int32_t) keep; - r->offset += len; break; } - case 0x9da7: /* note */ + case 0x3bf8fbbad1587cddull: /* note */ { - if ( kind != 13 ) + if ( kind != 17 ) { r->report->kind_mismatch++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } - break; - } - uint32_t body_len; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { r->report->malformed = 1; return 0; } - if ( depth >= kTableMaxDepth ) - { - /* past the nesting cap: the subtree is refused, the pointer stays - null, and the parent reads on (docs/SPEC-TABLES.md §4) */ - r->report->malformed = 1; - r->offset += body_len; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } break; } - { - Tally * pointee = tally_emplace( sink, &value->note ); - TableReader sub; - if ( pointee == NULL ) - { - r->report->malformed = 1; /* the caller's region was short */ - r->offset += body_len; - break; - } - sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - tally_load_body( &sub, pointee ); + retention=table_retain_step(body_keep,1,0); + { uint64_t index; + if(!table_reader_leb(&(*r),&index)) { r->report->malformed=1; + return 0; } - r->offset += body_len; + table_node_resolve((*r).nodes,&value->note,index,UINT64_C(0x69ff34904242a73d),r->report); + } break; } - default: - { - r->report->unknown++; - if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } break; - } + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; } } } -/* tally_pack_measure: the packed region bytes of everything Tally POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t tally_pack_measure( const TableCtx * ctx, const Tally * value, int32_t depth ) +static SCHEMA_UNUSED int marker_load_message_body_retain(TableMessageReader * r,Marker * value, TableRetainWalk retention) { - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - (void) ctx; (void) value; /* no pointers below this node */ - return bytes; + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + marker_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x39f7fcec8fcb623d): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,8); + memcpy(value->label,text,(size_t)kept); + value->label[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->label_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x3bf8fbbad1587cdd): { + if(!(entry->kind==17 && entry->elem_kind==0)){if(0){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + {uint64_t index; + if(r->index_bits<1 || !table_bit_get(&r->bits,&index,r->index_bits))goto malformed; + table_node_resolve(r->nodes,&value->note,index,UINT64_C(0x69ff34904242a73d),r->report); + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; } - -/* tally_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. - - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int tally_pack( const TableCtx * ctx, const Tally * src, Tally * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) +static SCHEMA_UNUSED int schema_graphdemo_tally_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) { - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( Tally ) ); /* a plain struct, by construction */ - (void) ctx; (void) base; (void) capacity; (void) used; + const Tally * value=(const Tally *)storage; Tally * out=(Tally *)(void *)target; + (void)n; (void)value; (void)out; (void)at; return 1; } - -/* tally_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t tally_load_measure_body( TableReader * r, int32_t depth ) -{ - int64_t bytes = 0; - (void) r; (void) depth; /* nothing below this body allocates */ - return bytes; -} - -/* marker_pack_measure: the packed region bytes of everything Marker POINTS AT. - Aliasing is not preserved: two pointers to one node pack as two nodes, - exactly as they ride the wire as two bodies (docs/SPEC-TABLES.md §3). */ -static SCHEMA_UNUSED int64_t marker_pack_measure( const TableCtx * ctx, const Marker * value, int32_t depth ) +static SCHEMA_UNUSED int schema_graphdemo_tally_wire_extent_(TableReader r,int64_t * at) { - int64_t bytes = 0; - if ( depth > kTableMaxDepth ) { return -1; } /* a data cycle, or a chain past the cap */ - { - const Tally * pointee = tally_at( ctx, &value->note ); /* note */ - if ( pointee != NULL ) - { - int64_t inner = tally_pack_measure( ctx, pointee, depth + 1 ); - if ( inner < 0 ) { return -1; } - bytes += table_align_up64( (int64_t) sizeof( Tally ) ) + inner; + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + default: break; } + if(!table_reader_skip(&r,kind)) { return 1; } } - return bytes; } - -/* marker_pack: copy src into dst (already placed), then lay every pointee out - depth-first behind it, in FIELD ORDER, by bump allocation. - - The pre-order is what makes a region simple to reason about: a child - always lands after the slot naming it, so region deltas are strictly - positive and a packed region cannot contain a cycle. */ -static SCHEMA_UNUSED int marker_pack( const TableCtx * ctx, const Marker * src, Marker * dst, uint8_t * base, int64_t capacity, int64_t * used, int32_t depth ) +static SCHEMA_UNUSED int64_t schema_graphdemo_tally_wire_storage_(const TableReader * r) { - if ( depth > kTableMaxDepth ) { return 0; } - memcpy( (void *) dst, (const void *) src, sizeof( Marker ) ); /* a plain struct, by construction */ - { - const Tally * pointee; - dst->note.value = 0; /* note */ - pointee = tally_at( ctx, &src->note ); - if ( pointee != NULL ) - { - int64_t at = table_align_up64( *used ); - Tally * child; - if ( at + (int64_t) sizeof( Tally ) > capacity ) { return 0; } - *used = at + table_align_up64( (int64_t) sizeof( Tally ) ); - child = (Tally *) (void *) ( base + at ); - dst->note.value = (int64_t) ( ( base + at ) - (const uint8_t *) (const void *) &dst->note ); - if ( !tally_pack( ctx, pointee, child, base, capacity, used, depth + 1 ) ) { return 0; } + int64_t at=sizeof(Tally); if(!schema_graphdemo_tally_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_marker_extent_(TableNumbering * n,const void * storage,uint8_t * target,int64_t * at) +{ + const Marker * value=(const Marker *)storage; Marker * out=(Marker *)(void *)target; + (void)n; (void)value; (void)out; (void)at; + if(target!=NULL) { const void * node=table_ref_at(n->ctx,&value->note); uint64_t index=table_number_find(n,node); + if(node!=NULL && index==0) { return 0; } out->note.value=index ? (int64_t)((n->pack_base+n->entries[index-1].packed_offset)-(uint8_t *)(void *)&out->note) : 0; } + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_marker_wire_extent_(TableReader r,int64_t * at) +{ + (void)at; + for(;;) { uint64_t ref,id; uint8_t kind; + if(!table_reader_leb(&r,&ref) || ref==0 || ref>r.id_count || !table_reader_has(&r,1)) { return 1; } + id=table_reader_id_at(&r,ref); kind=table_reader_get8(&r); + switch(id) { + default: break; } + if(!table_reader_skip(&r,kind)) { return 1; } } +} +static SCHEMA_UNUSED int64_t schema_graphdemo_marker_wire_storage_(const TableReader * r) +{ + int64_t at=sizeof(Marker); if(!schema_graphdemo_marker_wire_extent_(*r,&at) || at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int schema_graphdemo_tally_node_save_( TableWriter * w, const void * storage ) +{ return tally_save_body(w,(const Tally *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_tally_graph_edges_( TableNumbering * numbering, const void * storage ) +{ + const Tally * value=(const Tally *)storage; + (void)value; (void)numbering; return 1; } +static const TableNodeType schema_graphdemo_tally_node_type_ = { UINT64_C(0x69ff34904242a73d), sizeof(Tally), schema_graphdemo_tally_node_save_, schema_graphdemo_tally_graph_edges_, 0, schema_graphdemo_tally_extent_, schema_graphdemo_tally_wire_storage_, 4, 0, schema_graphdemo_tally_cook_body_, NULL }; -/* marker_load_measure_body: region bytes for the nodes under this wire body. - Framing only — no field value is decoded, so a caller can size its - buffer before a single byte is placed. */ -static SCHEMA_UNUSED int64_t marker_load_measure_body( TableReader * r, int32_t depth ) +static SCHEMA_UNUSED int schema_graphdemo_marker_node_save_( TableWriter * w, const void * storage ) +{ return marker_save_body(w,(const Marker *)storage); } +static SCHEMA_UNUSED int schema_graphdemo_marker_graph_edges_( TableNumbering * numbering, const void * storage ) { - int64_t bytes = 0; - for ( ;; ) - { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { return bytes; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return bytes; } - if ( !table_reader_has( r, 1 ) ) { return bytes; } - kind = table_reader_get8( r ); - switch ( field_id ) - { - case 0x9da7: /* note (*Tally) */ - { - uint32_t body_len; - if ( kind != 13 ) { if ( !table_reader_skip( r, kind ) ) { return bytes; } break; } - if ( !table_reader_has( r, 4 ) ) { return bytes; } - body_len = table_reader_get32( r ); - if ( !table_reader_has( r, body_len ) ) { return bytes; } - if ( depth < kTableMaxDepth ) - { - TableReader sub = table_reader_make( r->buffer + r->offset, body_len, r->report ); - bytes += table_align_up64( (int64_t) sizeof( Tally ) ); - bytes += tally_load_measure_body( &sub, depth + 1 ); - } - r->offset += body_len; - break; - } - default: - { - if ( !table_reader_skip( r, kind ) ) { return bytes; } - break; - } - } - } + const Marker * value=(const Marker *)storage; + (void)value; (void)numbering; + if (!table_number_slot(numbering,&value->note,&schema_graphdemo_tally_node_type_)) { return 0; } + return 1; } +static const TableNodeType schema_graphdemo_marker_node_type_ = { UINT64_C(0xd6458a3eef83d457), sizeof(Marker), schema_graphdemo_marker_node_save_, schema_graphdemo_marker_graph_edges_, 0, schema_graphdemo_marker_extent_, schema_graphdemo_marker_wire_storage_, 8, 0, schema_graphdemo_marker_cook_body_, NULL }; /* ---- Marker: the variable-length life (docs/SPEC-TABLES.md §2, §6, §9) ---- @@ -1397,10 +4095,10 @@ typedef struct MarkerBuilder /* Allocate a node in THIS thread's front: no lock, no atomic per node. One worker per thread; allocate on your own, and synchronize your own writes to nodes another worker allocated (§6.4). */ -static SCHEMA_UNUSED int marker_builder_init( MarkerBuilder * builder ) +static SCHEMA_UNUSED int marker_builder_init_with_allocator( MarkerBuilder * builder, TableAllocator allocator ) { uint32_t at; - table_arena_init( &builder->arena ); + table_arena_init_with_allocator( &builder->arena, allocator ); builder->main = table_worker_make( &builder->arena ); builder->root_ref.value = 0; builder->region = NULL; @@ -1415,10 +4113,12 @@ static SCHEMA_UNUSED int marker_builder_init( MarkerBuilder * builder ) return 1; } +static SCHEMA_UNUSED int marker_builder_init(MarkerBuilder * builder) { return marker_builder_init_with_allocator(builder,table_default_allocator()); } + static SCHEMA_UNUSED void marker_builder_shutdown( MarkerBuilder * builder ) { table_arena_shutdown( &builder->arena ); - free( builder->region ); + table_release(builder->arena.allocator,builder->region); builder->region = NULL; builder->region_bytes = 0; } @@ -1430,119 +4130,662 @@ static SCHEMA_UNUSED Marker * marker_builder_root( MarkerBuilder * builder ) return (Marker *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); } -/* Lock is ONE WAY and it is the compaction: the segmented arena becomes one - exact-packed region with zero slack, references rewritten self-relative, - and the mutable life released. Single-threaded: call it after the workers - have joined. The region comes back in builder->region. */ -static SCHEMA_UNUSED int marker_builder_lock( MarkerBuilder * builder ) -{ - TableCtx ctx; - const Marker * root; - int64_t below, total, used; - uint8_t * packed; - if ( builder->arena.locked ) { return builder->region != NULL; } - if ( builder->root_ref.value == 0 ) { return 0; } - ctx.arena = &builder->arena; - root = (const Marker *) (void *) table_arena_at( &builder->arena, (uint32_t) builder->root_ref.value ); - below = marker_pack_measure( &ctx, root, 1 ); - if ( below < 0 ) { return 0; } /* a data cycle, or a chain past kTableMaxDepth */ - total = table_align_up64( (int64_t) sizeof( Marker ) ) + below; - packed = (uint8_t *) malloc( (size_t) total ); /* the AUTHORING path may allocate */ - if ( packed == NULL ) { return 0; } - memset( packed, 0, (size_t) total ); - used = table_align_up64( (int64_t) sizeof( Marker ) ); - if ( !marker_pack( &ctx, root, (Marker *) (void *) packed, packed, total, &used, 1 ) || used != total ) - { - free( packed ); +static SCHEMA_UNUSED int marker_builder_lock(MarkerBuilder * builder) +{ + TableCtx ctx; const Marker * root; uint8_t * packed; int64_t bytes; + if (builder->arena.locked) { return builder->region!=NULL; } + if (builder->root_ref.value==0) { return 0; } + ctx.arena=&builder->arena; root=(const Marker *)table_arena_at(&builder->arena,(uint32_t)builder->root_ref.value); + packed=table_graph_pack(&ctx,root,&schema_graphdemo_marker_node_type_,&bytes); + if (packed==NULL) { return 0; } + builder->region=packed; builder->region_bytes=bytes; builder->arena.locked=1; + table_arena_shutdown(&builder->arena); return 1; +} + +static SCHEMA_UNUSED const TableNodeType * schema_graphdemo_marker_node_lookup_(uint64_t id) +{ + switch (id) { + case UINT64_C(0x69ff34904242a73d): return &schema_graphdemo_tally_node_type_; + default: return NULL; + } +} +static SCHEMA_UNUSED int64_t marker_measure_with_allocator(const TableCtx * ctx, const Marker * root, TableAllocator allocator) +{ + return table_graph_save(ctx,root,&schema_graphdemo_marker_node_type_,NULL,INT64_MAX,allocator); +} +static SCHEMA_UNUSED int64_t marker_measure(const TableCtx * ctx,const Marker * root) +{ return marker_measure_with_allocator(ctx,root,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t marker_save_with_allocator(const TableCtx * ctx, const Marker * root, uint8_t * buffer, int64_t capacity, TableAllocator allocator) +{ + if (buffer==NULL || capacity<0) { return -1; } + return table_graph_save(ctx,root,&schema_graphdemo_marker_node_type_,buffer,capacity,allocator); +} +static SCHEMA_UNUSED int64_t marker_save(const TableCtx * ctx,const Marker * root, uint8_t * buffer, int64_t capacity) +{ return marker_save_with_allocator(ctx,root,buffer,capacity,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int64_t schema_graphdemo_marker_load_layout_(const TableReader * reader, int64_t * data_out, int64_t * records_out) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id; + int64_t data=schema_graphdemo_marker_wire_storage_(reader), records=0; + if(data<0) { return -1; } + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_marker_node_lookup_(id); + if (type!=NULL) { int64_t size=table_node_wire_storage(type,&body); if (size<0 || size>INT64_MAX-data) { return -1; } data+=size; } + records++; + } + if (records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1 || (records+1)*(int64_t)sizeof(TableNodeDirEntry)>INT64_MAX-data) { return -1; } + *data_out=data; *records_out=records; return data+(records+1)*(int64_t)sizeof(TableNodeDirEntry); +} +static SCHEMA_UNUSED int64_t marker_load_measure_ex(const uint8_t * wire, int64_t bytes, int64_t * attribution, TableRefuseReason * reason) +{ + TableReport report; TableReader reader; int64_t data,records,total; memset(&report,0,sizeof(report)); + if (attribution!=NULL) { *attribution=0; } + if (!table_wire_open(&reader,wire,bytes,&report)) { if(report.refused && reason!=NULL) { *reason=SCHEMA_TABLE_REFUSE_UNKNOWN_FORM; } return -1; } + report.reason=SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH; total=schema_graphdemo_marker_load_layout_(&reader,&data,&records); + if(total<0 && reason!=NULL) { *reason=report.reason; } + if(total>=0 && attribution!=NULL) { *attribution=(records+1)*(int64_t)sizeof(TableNodeDirEntry); } return total; +} +static SCHEMA_UNUSED int64_t marker_load_measure(const uint8_t * wire, int64_t bytes) +{ return marker_load_measure_ex(wire,bytes,NULL,NULL); } +static SCHEMA_UNUSED int schema_graphdemo_marker_load_graph_(TableReader * reader, Marker * root, TableNodeMap * nodes, TableNodeDirEntry * directory, TableSink * sink) +{ + TableNodeScan scan=table_node_scan_begin(reader); TableReader body; uint64_t id,k=0; int32_t unknown=0; + marker_reset(root); + while (table_node_scan_next(&scan,&id,&body)) { + const TableNodeType * type=schema_graphdemo_marker_node_lookup_(id); + directory[k+1].type_id=id; directory[k+1].offset=UINT64_MAX; + if (type!=NULL && type->blob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; k++; continue; } + if (type==NULL) { unknown++; } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; return 0; } + directory[k+1].offset=(uint64_t)at; sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; return 0; } + directory[k+1].offset=at; + } + k++; + } + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; } else { reader->report->malformed=1; } + if(nodes->good) { + scan=table_node_scan_begin(reader); k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; TableSink carve; const TableNodeType * type=schema_graphdemo_marker_node_lookup_(id); + carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; extent.used=type->size; extent.capacity=table_node_wire_storage(type,&body); carve.region=&extent; } + nodes->sink=&carve; body.nodes=nodes; + switch(id) { + case UINT64_C(0x69ff34904242a73d): tally_load_body(&body,(Tally *)value); break; + default: break; + } + nodes->sink=NULL; if(nodes->refused) { return 0; } + } + k++; + } + } + reader->nodes=nodes; reader->nested=0; + { int read_ok; TableRegionSink extent; TableSink carve; carve.worker=sink->worker; carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; extent.used=sizeof(*root); extent.capacity=schema_graphdemo_marker_wire_storage_(reader); carve.region=&extent; } + nodes->sink=&carve; read_ok=marker_load_body(reader,root); nodes->sink=NULL; return !nodes->refused && (!nodes->arena || read_ok); } +} +static SCHEMA_UNUSED const Marker * marker_load(uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableRegionSink place; TableSink sink; int64_t data,records,total; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return NULL; } + total=schema_graphdemo_marker_load_layout_(&reader,&data,&records); + if(total<0 || region==NULL || region_bytesmalformed=1; return NULL; } + memset(region,0,(size_t)total); directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; directory[0].type_id=UINT64_C(0xd6458a3eef83d457); + nodes.base=region; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=0; nodes.refused=0; nodes.sink=NULL; + place.base=region; place.capacity=data; place.used=schema_graphdemo_marker_wire_storage_(&reader); sink.region=&place; sink.worker=NULL; + if(!schema_graphdemo_marker_load_graph_(&reader,(Marker *)(void *)region,&nodes,directory,&sink)) { return NULL; } + return (const Marker *)(const void *)region; +} +static SCHEMA_UNUSED int marker_load_builder(MarkerBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report) +{ + TableReport ignored; TableReader reader; TableNodeMap nodes; TableNodeDirEntry * directory; TableSink sink; TableNodeScan scan; TableReader body; uint64_t id; int64_t records=0; Marker * root; int ok; + memset(&ignored,0,sizeof(ignored)); if(report==NULL) { report=&ignored; } + if(!table_wire_open(&reader,wire,wire_bytes,report)) { return 0; } + scan=table_node_scan_begin(&reader); while(table_node_scan_next(&scan,&id,&body)) { records++; } + if(records>=INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)-1) { report->malformed=1; return 0; } + root=marker_builder_root(builder); if(root==NULL) { report->malformed=1; return 0; } + directory=(TableNodeDirEntry *)table_allocate(builder->arena.allocator,(records+1)*(int64_t)sizeof(TableNodeDirEntry)); + if(directory==NULL) { report->malformed=1; return 0; } + directory[0].offset=(uint64_t)builder->root_ref.value; directory[0].type_id=UINT64_C(0xd6458a3eef83d457); + nodes.base=NULL; nodes.entries=directory; nodes.count=(uint64_t)records+1; nodes.good=0; nodes.arena=1; nodes.refused=0; nodes.sink=NULL; sink.region=NULL; sink.worker=&builder->main; + ok=schema_graphdemo_marker_load_graph_(&reader,root,&nodes,directory,&sink); table_release(builder->arena.allocator,directory); return ok; +} +static SCHEMA_UNUSED int schema_graphdemo_marker_message_graph_save_(TableBitWriter * w,const Marker * root,TableAllocator allocator) +{ + TableNumbering numbering; + uint64_t i; + int ok=0; + if(!table_number_build(&numbering,NULL,root,&schema_graphdemo_marker_node_type_,allocator))goto done; + w->nodes=&numbering; + w->index_bits=table_bits_required(0,(int64_t)numbering.count); + if(numbering.count-1>UINT32_MAX)goto done; + if(numbering.count>1){ + table_bit_put(w,37,kTableMessageRefBitsHere); + table_bit_put(w,numbering.count-1,32); + for(i=1;itype->id){ + case UINT64_C(0x69ff34904242a73d): + table_bit_put(w,50,kTableMessageRefBitsHere); + if(!tally_save_message_body(w,(const Tally *)node->value))goto done; + break; + default:goto done; + } } }ok=marker_save_message_body(w,root); + done:table_number_dispose(&numbering); + w->nodes=NULL; + return ok&&!w->overflow; +} +static SCHEMA_UNUSED int64_t marker_measure_messages_with_allocator(const Marker * const * roots,int64_t count,TableReport * report,TableAllocator allocator) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(roots==NULL||count<1)return -1; + if(count>kTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax){if(report){report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL||capacity<0)return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid; + if(*id==UINT64_C(0x2f2ec0474f1c4fe4)||*id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_message_blob_scan(r,length))return 0; + return *size>=0; + }switch(*id){ + case UINT64_C(0x69ff34904242a73d):at=sizeof(Tally); + if(!schema_graphdemo_tally_message_extent_(r,&at))return 0; + break; + default:return table_message_skip_body(r); + }if(at>INT64_MAX-kTableAlign)return 0; + *size=table_align_up64(at); + return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_marker_message_storage_(TableMessageReader * r,uint64_t * records,int64_t * data,int64_t * root_bytes,int * complete) +{ + uint64_t i,id,length; + int64_t size,root=sizeof(Marker); + *data=0; + *complete=1; + if(!table_message_nodes_open(r,records))return 0; + for(i=0;i<*records;i++){ + if(!schema_graphdemo_marker_message_record_scan_(r,&id,&size,&length)||size>INT64_MAX-*data)return 0; + *data+=size; + } + if(!schema_graphdemo_marker_message_extent_(r,&root))*complete=0; + if(r->extent_refused)return 0; + if(root>INT64_MAX-kTableAlign)return 0; + *root_bytes=table_align_up64(root); + if(*root_bytes>INT64_MAX-*data)return 0; + *data+=*root_bytes; + return 1; +} +static SCHEMA_UNUSED int64_t marker_load_measure_messages_ex(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,int64_t * attribution,TableRefuseReason * reason) +{ + TableReport report={0}; + TableMessageReader r; + int64_t total=0,dirs=0,bodies,i; + if(attribution)*attribution=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,&report); + if(bodies<0)return -1; + for(i=0;i=(uint64_t)(INT64_MAX/(int64_t)sizeof(TableNodeDirEntry)))return -1; + dir=(int64_t)(records+1)*(int64_t)sizeof(TableNodeDirEntry); + if(data>INT64_MAX-dir || data+dir>INT64_MAX-total)return -1; + total+=data+dir; + dirs+=dir; + if(!complete)break; + }if(attribution)*attribution=dirs; + return total; +} +static SCHEMA_UNUSED int64_t marker_load_measure_messages(const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes) +{ return marker_load_measure_messages_ex(vocabulary,buffer,bytes,NULL,NULL); +} +static SCHEMA_UNUSED int schema_graphdemo_marker_message_load_into_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Marker ** out) +{ + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Marker); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Marker * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Marker *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0xd6458a3eef83d457); + *used+=root_size; + marker_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0x69ff34904242a73d):carve.used=sizeof(Tally); + carve.capacity=sizeof(Tally); + scan=*r; + if(!schema_graphdemo_tally_message_extent_(&scan,&carve.capacity))goto malformed; + if(!tally_load_message_body(r,(Tally *)(void *)value))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=marker_load_message_body(r,root); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int marker_load_messages(const Marker ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t bodies,capacity,used=0,i; + if(report==NULL)report=&ignored; + if(roots==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(region==NULL||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;iblob && body.size>UINT32_MAX) { reader->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; return 0; + } + if (type!=NULL && type->blob==2 && !table_wire_utf8(body.buffer,body.size)) { reader->report->malformed=1; + k++; + continue; + } + if (type==NULL) { unknown++; + } + else if (sink->region!=NULL) { + int64_t at=sink->region->used, size=table_node_wire_storage(type,&body); + if(size<0 || size>sink->region->capacity-at) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=(uint64_t)at; + sink->region->used+=size; + } else { + uint32_t at=table_worker_alloc(sink->worker,type->blob ? table_node_wire_storage(type,&body) : type->size); + if(at==kTableAllocFailed) { reader->report->malformed=1; + return 0; + } + directory[k+1].offset=at; + } + k++; } - builder->region = packed; - builder->region_bytes = total; - builder->arena.locked = 1; /* MONOTONIC: there is no unlock */ - table_arena_shutdown( &builder->arena ); - return 1; + nodes->good=table_node_scan_whole(&scan); + if(nodes->good) { reader->report->unknown+=unknown; + if(retention.retain)reader->report->retain_lost+=unknown; + } else { reader->report->malformed=1; + } + if(nodes->good) { + scan=table_node_scan_begin(reader); + k=0; + while(table_node_scan_next(&scan,&id,&body)) { + if(directory[k+1].offset!=UINT64_MAX) { + void * value=nodes->arena ? table_arena_at(sink->worker->arena,(uint32_t)directory[k+1].offset) : nodes->base+directory[k+1].offset; + TableRegionSink extent; + TableSink carve; + const TableNodeType * type=schema_graphdemo_marker_node_lookup_(id); + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)value; + extent.used=type->size; + extent.capacity=table_node_wire_storage(type,&body); + carve.region=&extent; + } + nodes->sink=&carve; + body.nodes=nodes; + switch(id) { + case UINT64_C(0x69ff34904242a73d):retention.path=table_retain_root(value,(uint32_t)k+2); + tally_load_body_retain(&body,(Tally *)value, retention); + break; + default: break; + } + nodes->sink=NULL; + if(nodes->refused) { return 0; + } + } + k++; + } + } + reader->nodes=nodes; + reader->nested=0; + { int read_ok; + TableRegionSink extent; + TableSink carve; + carve.worker=sink->worker; + carve.region=NULL; + if(!nodes->arena) { extent.base=(uint8_t *)(void *)root; + extent.used=sizeof(*root); + extent.capacity=schema_graphdemo_marker_wire_storage_(reader); + carve.region=&extent; + } + nodes->sink=&carve; + retention.path=table_retain_root(root,1); + read_ok=marker_load_body_retain(reader,root, retention); + nodes->sink=NULL; + return !nodes->refused && (!nodes->arena || read_ok); + } } - -/* ---- Marker on the wire: the generic, tolerant form (docs/SPEC-TABLES.md §3) ---- - - The CONTEXT says which form the root is in: NULL, or a ctx with a NULL - arena, is a packed REGION — what Lock and Load produce — and a ctx naming - a builder's arena is the mutable form. C++ spells the two as overloads; - one parameter says the same thing here. */ -static SCHEMA_UNUSED int64_t marker_measure( const TableCtx * ctx, const Marker * root ) +static SCHEMA_UNUSED const Marker * marker_load_retain(uint8_t * region,int64_t region_bytes,const uint8_t * wire,int64_t wire_bytes,TableRetain * retain,TableReport * report) { - return root != NULL ? marker_measure_body( ctx, root, 1 ) : -1; + TableReport ignored={0}; + TableReader reader; + TableNodeMap nodes; + TableNodeDirEntry * directory; + TableRegionSink place; + TableSink sink; + TableRetainWalk retention; + int64_t data,records,total; + if(report==NULL)report=&ignored; + table_retain_reset(retain,NULL,NULL); + if(!table_wire_open(&reader,wire,wire_bytes,report))return NULL; + total=schema_graphdemo_marker_load_layout_(&reader,&data,&records); + if(total<0||region==NULL||region_bytesmalformed=1; + return NULL; + } + memset(region,0,(size_t)total); + directory=(TableNodeDirEntry *)(void *)(region+data); + directory[0].offset=0; + directory[0].type_id=UINT64_C(0xd6458a3eef83d457); + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=(uint64_t)records+1; + place.base=region; + place.capacity=data; + place.used=schema_graphdemo_marker_wire_storage_(&reader); + sink.region=&place; + sink.worker=NULL; + table_retain_reset(retain,&nodes,region); + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.path=table_retain_root(region,1); + if(!schema_graphdemo_marker_load_graph_retain_(&reader,(Marker *)(void *)region,&nodes,directory,&sink,retention))return NULL; + return (const Marker *)(const void *)region; } - -static SCHEMA_UNUSED int64_t marker_save( const TableCtx * ctx, const Marker * root, uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED int schema_graphdemo_marker_retain_save_node_(TableWriter * w,const void * value,uint64_t id,TableRetainWalk retention) { - TableWriter w; - if ( root == NULL ) { return -1; } - w = table_writer_make( buffer, capacity ); - if ( !marker_save_body( ctx, &w, root, 1 ) ) { return -1; } - return w.offset; /* == marker_measure( ctx, root ) */ + switch(id){ + case UINT64_C(0xd6458a3eef83d457):return marker_save_body_retain(w,(const Marker *)value,retention); + case UINT64_C(0x69ff34904242a73d):return tally_save_body_retain(w,(const Tally *)value,retention); + default:return 0; + } } - -/* marker_load_measure: the exact region bytes a wire buffer will need. The - caller owns the allocation — generated load code allocates nothing. */ -static SCHEMA_UNUSED int64_t marker_load_measure( const uint8_t * wire, int64_t wire_bytes ) +static SCHEMA_UNUSED int64_t marker_measure_retain_with_allocator(const Marker * root,TableRetain * retain,TableAllocator allocator) +{if(!root)return -1; +return table_retain_graph_save(root,&schema_graphdemo_marker_node_type_,retain,NULL,INT64_MAX,NULL,allocator,schema_graphdemo_marker_retain_save_node_); +} +static SCHEMA_UNUSED int64_t marker_measure_retain(const Marker * root,TableRetain * retain) +{return marker_measure_retain_with_allocator(root,retain,table_default_allocator()); +} +static SCHEMA_UNUSED int64_t marker_save_retain_with_allocator(const Marker * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator) +{if(!root||!buffer||capacity<0||!report)return -1; +return table_retain_graph_save(root,&schema_graphdemo_marker_node_type_,retain,buffer,capacity,report,allocator,schema_graphdemo_marker_retain_save_node_); +} +static SCHEMA_UNUSED int64_t marker_save_retain(const Marker * root,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report) +{return marker_save_retain_with_allocator(root,retain,buffer,capacity,report,table_default_allocator()); +} +static SCHEMA_UNUSED int schema_graphdemo_marker_message_load_into_retain_(TableMessageReader * r,uint8_t * region,int64_t capacity,int64_t * used,const Marker ** out, TableRetainWalk retention) { - TableReport ignored; - TableReader r; - int64_t below; - memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( wire, wire_bytes, &ignored ); - below = marker_load_measure_body( &r, 1 ); - if ( below < 0 ) { below = 0; } - return table_align_up64( (int64_t) sizeof( Marker ) ) + below; -} - -/* marker_load: decode the tolerant wire into the caller's exact-sized region and - return the root. Partial results are kept, as everywhere on this wire — - the report says what happened. NULL means the CALLER's buffer was wrong. */ -static SCHEMA_UNUSED const Marker * marker_load( uint8_t * region, int64_t region_bytes, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - TableRegionSink region_sink; - TableSink sink; - Marker * root; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - if ( region == NULL || region_bytes < (int64_t) sizeof( Marker ) ) { out->malformed = 1; return NULL; } - if ( ( ( (uintptr_t) region ) & ( kTableAlign - 1 ) ) != 0 ) { out->malformed = 1; return NULL; } - memset( region, 0, (size_t) region_bytes ); - region_sink.base = region; - region_sink.capacity = region_bytes; - region_sink.used = table_align_up64( (int64_t) sizeof( Marker ) ); - sink.region = ®ion_sink; - sink.worker = NULL; - root = (Marker *) (void *) region; - r = table_reader_make( wire, wire_bytes, out ); - marker_load_body( &r, &sink, root, 1 ); - return root; -} - -/* marker_load_builder: the TOOL's path — the same tolerant decode into a fresh - builder, so loaded data can be edited and locked again. */ -static SCHEMA_UNUSED int marker_load_builder( MarkerBuilder * builder, const uint8_t * wire, int64_t wire_bytes, TableReport * report ) -{ - TableReport ignored; - TableReport * out; - Marker * root; - TableSink sink; - TableReader r; - memset( &ignored, 0, sizeof( ignored ) ); - out = report != NULL ? report : &ignored; - root = marker_builder_root( builder ); - if ( root == NULL ) { out->malformed = 1; return 0; } - sink.region = NULL; - sink.worker = &builder->main; - r = table_reader_make( wire, wire_bytes, out ); - return marker_load_body( &r, &sink, root, 1 ); + (void)retention; + + uint64_t count,i,id,length; + int64_t dir_bytes,records_start,fields_start,size,root_size=sizeof(Marker); + TableNodeDirEntry * directory; + TableNodeMap nodes; + TableRegionSink carve; + TableSink sink; + TableMessageReader scan; + Marker * root; + int ok; + if(!table_message_nodes_open(r,&count))goto malformed; + dir_bytes=(int64_t)(count+1)*(int64_t)sizeof(TableNodeDirEntry); + if(*used>capacity||dir_bytes>capacity-*used)goto malformed; + directory=(TableNodeDirEntry *)(void *)(region+*used); + *used+=dir_bytes; + memset(&nodes,0,sizeof(nodes)); + nodes.base=region; + nodes.entries=directory; + nodes.count=count+1; + records_start=r->bits.offset; + for(i=0;ireport->unknown++; + r->report->retain_lost += retention.retain != NULL; + continue; + }if(size>capacity-*used)goto malformed; + directory[i+1].offset=(uint64_t)*used; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){TableBlob * blob=(TableBlob *)(void *)(region+*used); + blob->length=(uint32_t)length; + blob->zero=0; + }*used+=size; + } + fields_start=r->bits.offset; + scan=*r; + (void)scan; + if(root_size>INT64_MAX-kTableAlign)goto malformed; + root_size=table_align_up64(root_size); + if(root_size>capacity-*used)goto malformed; + root=(Marker *)(void *)(region+*used); + directory[0].offset=(uint64_t)*used; + directory[0].type_id=UINT64_C(0xd6458a3eef83d457); + *used+=root_size; + marker_reset(root); + *out=root; + nodes.good=1; + r->nodes=&nodes; + sink.worker=NULL; + sink.region=&carve; + nodes.sink=&sink; + r->bits.offset=records_start; + table_retain_reset(retention.retain,&nodes,region); + for(i=0;iid; + if(id==UINT64_C(0x2f2ec0474f1c4fe4)||id==UINT64_C(0x704be0d8faaffc58)){ + if(!table_bit_get(&r->bits,&length,32)||!table_bit_align_read(&r->bits)||!table_bit_has(&r->bits,(int64_t)length*8))goto malformed; + if(directory[i+1].offset!=UINT64_MAX)memcpy(region+directory[i+1].offset+sizeof(TableBlob),r->bits.buffer+(r->bits.offset>>3),(size_t)length); + r->bits.offset+=(int64_t)length*8; + continue; + } + if(directory[i+1].offset==UINT64_MAX){if(!table_message_skip_body(r))goto malformed; + continue; + } + value=region+directory[i+1].offset; + carve.base=value; + switch(id){ + case UINT64_C(0x69ff34904242a73d):retention.path=table_retain_root(value,(uint32_t)i+2); + /* type 0x69ff34904242a73d */carve.used=sizeof(Tally); + carve.capacity=sizeof(Tally); + scan=*r; + if(!schema_graphdemo_tally_message_extent_(&scan,&carve.capacity))goto malformed; + if(!tally_load_message_body_retain(r,(Tally *)(void *)value, retention))goto malformed; + break; + default:goto malformed; + } + }if(r->bits.offset!=fields_start)goto malformed; + retention.path=table_retain_root(root,1); + carve.base=(uint8_t *)(void *)root; + carve.used=sizeof(*root); + carve.capacity=root_size; + ok=marker_load_message_body_retain(r,root, retention); + r->nodes=NULL; + if(!ok)return 0; + *out=root; + return 1; + malformed:r->nodes=NULL; + r->report->malformed=1; + return 0; } - +static SCHEMA_UNUSED int marker_load_retain_messages(const Marker ** roots,int64_t * count,uint8_t * region,int64_t region_bytes,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableRetain * retains,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + TableRetainWalk retention; + int64_t bodies,capacity,used=0,i; + if(!report)report=&ignored; + if(!roots||!count){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + if(!region||region_bytes<0||((uintptr_t)region&(kTableAlign-1))){report->malformed=1; + return 0; + }memset(region,0,(size_t)region_bytes); + for(i=0;ihits,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t tally_cook_measure(const Tally * value) { (void)value; return 88; } +static SCHEMA_UNUSED int tally_cook(const Tally * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<88) { return 0; } + memset(raw,0,88); + if(!schema_graphdemo_tally_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+80,UINT64_C(0x69ff34904242a73d),8,order); + table_cook_header(raw,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,8,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_marker_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Marker * value=(const Marker *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->label,value->label_length,9); + table_cook_put(at+0+12,(uint32_t)value->label_length,4,order); + if(!table_cook_ref(n,at+16,&value->note,order)) { return 0; } + return 1; +} +static SCHEMA_UNUSED int64_t marker_cook_measure_with_allocator(const TableCtx * ctx,const Marker * value,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_marker_node_type_,NULL,0,TableByteOrder_Little,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,1); } +static SCHEMA_UNUSED int marker_cook_with_allocator(const TableCtx * ctx,const Marker * value,void * output,uint64_t capacity,TableByteOrder order,TableAllocator allocator) +{ return table_graph_cook(ctx,value,&schema_graphdemo_marker_node_type_,output,capacity,order,allocator,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,0)>=0; } +static SCHEMA_UNUSED int64_t marker_cook_measure(const TableCtx * ctx,const Marker * value) +{ return marker_cook_measure_with_allocator(ctx,value,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } +static SCHEMA_UNUSED int marker_cook(const TableCtx * ctx,const Marker * value,void * output,uint64_t capacity,TableByteOrder order) +{ return marker_cook_with_allocator(ctx,value,output,capacity,order,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()); } /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared @@ -1652,9 +4933,14 @@ static SCHEMA_UNUSED int64_t tally_to_json( const Tally * value, char * buffer, return schema_graphdemo_tally_to_json_( value, buffer, capacity ); } -/* Marker is VARIABLE-LENGTH. Its text form reads through the builder - (docs/SPEC-TABLES.md §16.1), which this backend does not emit yet: - no marker_from_json and no marker_to_json exist to call. */ +int schema_graphdemo_marker_from_json_( MarkerBuilder * builder, const char * text, int64_t bytes, TableReport * report ); +int64_t schema_graphdemo_marker_to_json_( const Marker * value, char * buffer, int64_t capacity, TableAllocator allocator ); +static SCHEMA_UNUSED int marker_from_json( MarkerBuilder * builder, const char * text, int64_t bytes, TableReport * report ) { return schema_graphdemo_marker_from_json_(builder,text,bytes,report); } +static SCHEMA_UNUSED int64_t marker_to_json_measure( const Marker * value ) { return schema_graphdemo_marker_to_json_(value,NULL,0,table_default_allocator()); } +static SCHEMA_UNUSED int64_t marker_to_json( const Marker * value, char * buffer, int64_t capacity ) { return schema_graphdemo_marker_to_json_(value,buffer,capacity,table_default_allocator()); } + +static SCHEMA_UNUSED int64_t marker_to_json_with_allocator(const Marker * value,char * buffer,int64_t capacity,TableAllocator allocator) { return schema_graphdemo_marker_to_json_(value,buffer,capacity,allocator); } +static SCHEMA_UNUSED int64_t marker_to_json_measure_with_allocator(const Marker * value,TableAllocator allocator) { return schema_graphdemo_marker_to_json_(value,NULL,0,allocator); } #ifdef __cplusplus diff --git a/testdata/golden/tables/pointers-c/PartsTable.c b/testdata/golden/tables/pointers-c/PartsTable.c index c3a4fd681..5c17c2d16 100644 --- a/testdata/golden/tables/pointers-c/PartsTable.c +++ b/testdata/golden/tables/pointers-c/PartsTable.c @@ -78,7 +78,7 @@ static SCHEMA_UNUSED const char * table_json_variant_name( const TableFieldInfo return f->variants[value].name; } -static SCHEMA_UNUSED uint16_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) +static SCHEMA_UNUSED uint64_t table_json_variant_id( const TableFieldInfo * f, uint64_t value ) { if ( f->variants == NULL || f->enum_max < 0 || value > (uint64_t) f->enum_max ) { return 0; } return f->variants[value].id; @@ -90,7 +90,7 @@ static SCHEMA_UNUSED const char * table_json_key_name( const TableFieldInfo * f, return f->keys[key].name; } -static SCHEMA_UNUSED uint16_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) +static SCHEMA_UNUSED uint64_t table_json_key_id( const TableFieldInfo * f, uint64_t key ) { if ( f->keys == NULL || f->key_max < 0 || key > (uint64_t) f->key_max ) { return 0; } return f->keys[key].id; @@ -135,6 +135,121 @@ static SCHEMA_UNUSED int64_t table_json_get_signed( const void * storage, uint32 return (int64_t) raw; } +typedef struct TableJsonWide +{ + uint64_t lo; + uint64_t hi; +} TableJsonWide; + +static SCHEMA_UNUSED int table_json_kind_wide( uint8_t kind ) { return kind >= 18 && kind <= 29; } +static SCHEMA_UNUSED int table_json_kind_wide_signed( uint8_t kind ) { return kind == 18 || ( kind >= 20 && kind <= 24 ); } +static SCHEMA_UNUSED int table_json_kind_fixed( uint8_t kind ) { return kind >= 20 && kind <= 29; } + +static SCHEMA_UNUSED int table_json_wide_zero( TableJsonWide v ) { return v.lo == 0 && v.hi == 0; } +static SCHEMA_UNUSED int table_json_wide_negative( TableJsonWide v ) { return ( v.hi >> 63 ) != 0; } + +static SCHEMA_UNUSED int table_json_wide_compare( TableJsonWide a, TableJsonWide b, int is_signed ) +{ + if ( is_signed && table_json_wide_negative( a ) != table_json_wide_negative( b ) ) { return table_json_wide_negative( a ) ? -1 : 1; } + if ( a.hi != b.hi ) { return a.hi < b.hi ? -1 : 1; } + if ( a.lo != b.lo ) { return a.lo < b.lo ? -1 : 1; } + return 0; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shl( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.hi = v.lo << ( n - 64 ); return r; } + r.hi = ( v.hi << n ) | ( v.lo >> ( 64 - n ) ); + r.lo = v.lo << n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_shr( TableJsonWide v, int n ) +{ + TableJsonWide r = { 0, 0 }; + if ( n <= 0 ) { return v; } + if ( n >= 128 ) { return r; } + if ( n >= 64 ) { r.lo = v.hi >> ( n - 64 ); return r; } + r.lo = ( v.lo >> n ) | ( v.hi << ( 64 - n ) ); + r.hi = v.hi >> n; + return r; +} + +static SCHEMA_UNUSED TableJsonWide table_json_wide_neg( TableJsonWide v ) +{ + TableJsonWide r; + r.lo = ~v.lo + 1; + r.hi = ~v.hi + ( r.lo == 0 ? 1 : 0 ); + return r; +} + +// v = v * m + a; the return is the carry out of 128 bits +static SCHEMA_UNUSED uint32_t table_json_wide_mul_add( TableJsonWide * v, uint32_t m, uint32_t a ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t carry = a; + for ( int i = 0; i < 4; i++ ) + { + uint64_t p = limb[i] * m + carry; + limb[i] = p & 0xffffffffull; + carry = p >> 32; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) carry; +} + +// v = v / d; the return is the remainder +static SCHEMA_UNUSED uint32_t table_json_wide_div( TableJsonWide * v, uint32_t d ) +{ + uint64_t limb[4] = { v->lo & 0xffffffffull, v->lo >> 32, v->hi & 0xffffffffull, v->hi >> 32 }; + uint64_t rem = 0; + for ( int i = 3; i >= 0; i-- ) + { + uint64_t cur = ( rem << 32 ) | limb[i]; + limb[i] = cur / d; + rem = cur % d; + } + v->lo = limb[0] | ( limb[1] << 32 ); + v->hi = limb[2] | ( limb[3] << 32 ); + return (uint32_t) rem; +} + +// The storage of a wide kind, as lanes. A sixteen-byte storage is serialize's +// pair — native __int128 in the host's byte order, or the emulated struct with +// its low lane first — so the lanes are read in the host's order; a narrower +// storage is one lane, sign-extended for a signed kind. +static SCHEMA_UNUSED TableJsonWide table_json_wide_load( const void * storage, uint32_t width, int is_signed ) +{ + TableJsonWide v = { 0, 0 }; + if ( width == 16 ) + { + uint64_t half[2]; + memcpy( half, storage, 16 ); + v.lo = half[0]; v.hi = half[1]; + return v; + } + v.lo = is_signed ? (uint64_t) table_json_get_signed( storage, width ) : table_json_get_raw( storage, width ); + v.hi = ( is_signed && ( v.lo >> 63 ) != 0 ) ? ~(uint64_t) ( 0 ) : 0; + return v; +} + +static SCHEMA_UNUSED void table_json_wide_store( void * storage, uint32_t width, TableJsonWide v ) +{ + if ( width == 16 ) + { + uint64_t half[2]; + half[0] = v.lo; half[1] = v.hi; + memcpy( storage, half, 16 ); + return; + } + table_json_set_raw( storage, width, v.lo ); +} + + /* a counted field's companion: a string's length, a bytes' length, a counted array's count. Bounded by the declared extent on the way out, so a storage invariant a caller broke cannot walk off the end of the array. */ @@ -196,11 +311,11 @@ static SCHEMA_UNUSED uint64_t table_json_keyed_slot_key( int64_t slot ) /* A slot whose key names a variant of the keying enum. Every slot in [0, array_bound) does, unless the enum carries max-headroom variants outside - a table closure, where a reserved value names nothing and its key id is 0 — - the reserved id no declared name can fold to (§5). */ + a table closure. Such headroom has no declared name. A full identity may + be zero, so identity alone cannot distinguish a valid key. */ static SCHEMA_UNUSED int table_json_keyed_slot_valid( const TableFieldInfo * f, int64_t slot ) { - return table_json_key_id( f, table_json_keyed_slot_key( slot ) ) != 0; + return table_json_key_name( f, table_json_keyed_slot_key( slot ) ) != NULL; } static SCHEMA_UNUSED int table_json_is_flags( const TableFieldInfo * f ) @@ -215,11 +330,13 @@ static SCHEMA_UNUSED int table_json_is_enum( const TableFieldInfo * f ) static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) { - if ( f->kind == 12 ) { return 's'; } /* string */ + if ( f->kind == 12 || f->kind == 33 ) { return 's'; } /* string */ if ( table_json_is_bytes( f ) ) { return 's'; } /* bytes: base64 */ if ( table_json_is_keyed( f ) ) { return 'o'; } /* an object keyed by variant NAME */ + if ( f->sequence==2 ) { return 'o'; } if ( f->is_array ) { return 'a'; } if ( f->arms != NULL ) { return 'o'; } /* union: an object with ONE key */ + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } /* nested table or type */ if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -230,6 +347,8 @@ static SCHEMA_UNUSED char table_json_shape( const TableFieldInfo * f ) /* the ELEMENT shape of an array field — the same classifier one level down */ static SCHEMA_UNUSED char table_json_element_shape( const TableFieldInfo * f ) { + if ( f->arms != NULL ) { return 'o'; } + if ( f->kind == 17 ) { return f->table == NULL ? 's' : 'o'; } if ( f->kind == 13 ) { return 'o'; } if ( table_json_is_enum( f ) ) { return 's'; } if ( table_json_is_flags( f ) ) { return 'a'; } @@ -284,6 +403,7 @@ typedef struct TableJsonOut int64_t capacity; int64_t offset; int overflow; + void * graph; } TableJsonOut; static SCHEMA_UNUSED void table_json_raw( TableJsonOut * out, const char * data, int64_t count ) @@ -396,15 +516,9 @@ static SCHEMA_UNUSED int32_t table_json_utf8( const char * s, int32_t remaining, return code; } -/* A JSON text MUST be valid UTF-8 (RFC 8259 §8.1). The read path is - byte-transparent — the wire imposes no encoding (§3) and a string may hold - anything — so the WRITER is where that obligation is met: a byte that is - not part of a well-formed sequence is written as U+FFFD, one per bad byte, - and never raw. A text this walk writes is therefore readable by any - conforming parser, which a raw byte would not be. The cost is stated - plainly: for a string holding invalid UTF-8, the round trip is NOT - byte-identical, because the alternative is emitting a text that is not - JSON. */ +/* A JSON text must be valid UTF-8 (RFC 8259 section 8.1). Caller-filled storage can still contain + invalid bytes; write each such byte as U+FFFD rather than emitting invalid + JSON. The text reader also replaces ill-formed sequences before clamping. */ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const char * s, int32_t length ) { static const char hex[] = "0123456789abcdef"; @@ -453,6 +567,50 @@ static SCHEMA_UNUSED void table_json_write_string( TableJsonOut * out, const cha table_json_put( out, '"' ); } +static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit ); + +static SCHEMA_UNUSED void table_json_write_w_string( TableJsonOut * out, const uint16_t * s, int32_t length ) +{ + static const char hex[] = "0123456789abcdef"; + table_json_put( out, '"' ); + for ( int32_t i = 0; i < length; i++ ) + { + uint32_t code = (uint32_t) (uint16_t) s[i]; + if ( code >= 0xd800 && code <= 0xdbff && i + 1 < length ) + { + const uint32_t low = (uint32_t) (uint16_t) s[i + 1]; + if ( low >= 0xdc00 && low <= 0xdfff ) + { + code = 0x10000 + ( ( code - 0xd800 ) << 10 ) + ( low - 0xdc00 ); + i++; + } + } + if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } // an unpaired surrogate + switch ( code ) + { + case '"': table_json_raw( out, "\\\"", 2 ); continue; + case '\\': table_json_raw( out, "\\\\", 2 ); continue; + case '\b': table_json_raw( out, "\\b", 2 ); continue; + case '\f': table_json_raw( out, "\\f", 2 ); continue; + case '\n': table_json_raw( out, "\\n", 2 ); continue; + case '\r': table_json_raw( out, "\\r", 2 ); continue; + case '\t': table_json_raw( out, "\\t", 2 ); continue; + default: break; + } + if ( code < 0x20 ) + { + char escape[6] = { '\\', 'u', '0', '0', hex[ code >> 4 ], hex[ code & 0xf ] }; + table_json_raw( out, escape, 6 ); + continue; + } + char encoded[4]; + const int32_t encoded_length = table_json_encode_utf8( code, encoded ); + table_json_raw( out, encoded, encoded_length ); + } + table_json_put( out, '"' ); +} + + static SCHEMA_UNUSED void table_json_write_unsigned( TableJsonOut * out, uint64_t value ) { char digits[24]; @@ -484,6 +642,52 @@ static SCHEMA_UNUSED void table_json_write_signed( TableJsonOut * out, int64_t v readable. Non-finite values have no JSON spelling at all, and the writer REFUSES rather than losing one silently — the same rule measure and save already apply to an enum value no variant names (§5). */ +static SCHEMA_UNUSED void table_json_write_wide( TableJsonOut * out, const void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + TableJsonWide v = table_json_wide_load( storage, f->elem_size, is_signed ); + if ( is_signed && table_json_wide_negative( v ) ) + { + table_json_put( out, '-' ); + v = table_json_wide_neg( v ); + } + int frac = f->frac_bits; + TableJsonWide whole = table_json_wide_shr( v, frac ); + char digits[40]; + int32_t n = 0; + do + { + digits[n++] = (char) ( '0' + (int) table_json_wide_div( &whole, 10 ) ); + } while ( !table_json_wide_zero( whole ) ); + char text[40]; + for ( int32_t i = 0; i < n; i++ ) { text[i] = digits[n - 1 - i]; } + table_json_raw( out, text, n ); + if ( !table_json_kind_fixed( f->kind ) ) { return; } + table_json_put( out, '.' ); + // the fraction bits alone: v with everything at and above bit F cleared + TableJsonWide fraction = v; + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + if ( frac == 0 ) { fraction.lo = 0; } + if ( table_json_wide_zero( fraction ) ) + { + table_json_put( out, '0' ); + return; + } + while ( !table_json_wide_zero( fraction ) ) + { + // ×10: the digit is what lands at and above bit F, including the + // carry out of 128 bits when F leaves no room for it below + uint32_t carry = table_json_wide_mul_add( &fraction, 10, 0 ); + uint64_t digit = table_json_wide_shr( fraction, frac ).lo; + if ( frac > 64 ) { digit |= (uint64_t) ( carry ) << ( 128 - frac ); } + table_json_put( out, (char) ( '0' + (int) digit ) ); + if ( frac < 64 ) { fraction.hi = 0; fraction.lo &= ( (uint64_t) ( 1 ) << frac ) - 1; } + else { fraction.hi &= ( (uint64_t) ( 1 ) << ( frac - 64 ) ) - 1; } + } +} + + static SCHEMA_UNUSED int table_json_write_float( TableJsonOut * out, double value, int single ) { char text[64]; @@ -526,8 +730,22 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void /* one scalar, at one storage address: a nested object, a union, a vocabulary, or a number */ +static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ); +#else +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)out; (void)slot; (void)f; (void)depth; return 0; } +#endif static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void * storage, const TableFieldInfo * f, int32_t depth ) { + if ( f->kind == 17 ) { return table_json_write_pointer(out,storage,f,depth); } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; None is {} */ @@ -551,7 +769,10 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_line( out, depth + 1 ); table_json_write_string( out, arm, (int32_t) strlen( arm ) ); table_json_raw( out, ": ", 2 ); - if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) + if ( arms->arms[tag].field != NULL ) { + if ( !table_json_write_field( out, storage, arms->arms[tag].field, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table == NULL ) { table_json_raw( out, "null", 4 ); } + else if ( !table_json_write_value( out, (const uint8_t *) storage + arms->arms[tag].offset, arms->arms[tag].table, depth + 1 ) ) { return 0; } @@ -571,7 +792,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void wire identity: the writer REFUSES rather than writing None over it, the rule measure and save already apply (docs/SPEC-TABLES.md §5) */ if ( (int64_t) value > f->enum_max ) { return 0; } - if ( value != 0 && table_json_variant_id( f, value ) == 0 ) { return 0; } + if ( value != 0 && table_json_variant_name( f, value ) == NULL ) { return 0; } name = table_json_variant_name( f, value ); if ( name == NULL ) { return 0; } table_json_write_string( out, name, (int32_t) strlen( name ) ); @@ -607,6 +828,7 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void table_json_put( out, ']' ); return 1; } + if ( table_json_kind_wide( f->kind ) ) { table_json_write_wide( out, storage, f ); return 1; } switch ( f->kind ) { case 1: @@ -636,6 +858,15 @@ static SCHEMA_UNUSED int table_json_write_scalar( TableJsonOut * out, const void static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void * base, const TableFieldInfo * f, int32_t depth ) { const uint8_t * storage = (const uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_write_list(out,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_write_map(out,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + table_json_write_w_string( out, (const uint16_t *)(const void *)storage, table_json_count( base, f ) ); return 1; + } if ( f->kind == 12 ) { table_json_write_string( out, (const char *) storage, table_json_count( base, f ) ); @@ -703,9 +934,9 @@ static SCHEMA_UNUSED int table_json_write_field( TableJsonOut * out, const void /* One instance, every field, in DECLARATION ORDER, defaults included — a text is for people and tools, and a text that elides is a text a reader has to know the schema to complete. */ -static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_write_fields( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth, int * any ) { - int any = 0; + if ( depth > kTableJsonMaxDepth ) { return 0; } int32_t i; for ( i = 0; i < info->num_fields; i++ ) { @@ -719,24 +950,25 @@ static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void { continue; } - if ( !any ) { table_json_put( out, '{' ); } + if ( !*any ) { table_json_put( out, '{' ); } else { table_json_put( out, ',' ); } - any = 1; + *any = 1; table_json_line( out, depth + 1 ); table_json_write_string( out, f->json, (int32_t) strlen( f->json ) ); table_json_raw( out, ": ", 2 ); if ( !table_json_write_field( out, base, f, depth + 1 ) ) { return 0; } } - if ( !any ) - { - table_json_raw( out, "{}", 2 ); - return 1; - } - table_json_line( out, depth ); - table_json_put( out, '}' ); return 1; } +static SCHEMA_UNUSED int table_json_write_value( TableJsonOut * out, const void * base, const TableTypeInfo * info, int32_t depth ) +{ + int any = 0; + if ( !table_json_write_fields(out,base,info,depth,&any) ) { return 0; } + if ( !any ) { table_json_raw(out,"{}",2); return 1; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + /* ---- reading ---- */ typedef struct TableJsonIn @@ -746,6 +978,7 @@ typedef struct TableJsonIn int64_t pos; TableReport * report; int bad; /* the text is not JSON: the walk stops and keeps what it placed */ + void * graph; } TableJsonIn; static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) @@ -754,8 +987,21 @@ static SCHEMA_UNUSED void table_json_space( TableJsonIn * in ) { char c = in->text[in->pos]; if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) { in->pos++; continue; } - /* comments are not JSON, and a walk that guessed at one would be - reading a dialect nobody wrote down */ + /* SPEC-TABLES 16.2: comments are whitespace on read. */ + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '/' ) + { + in->pos += 2; + while ( in->pos < in->size && in->text[in->pos] != '\n' ) { in->pos++; } + continue; + } + if ( c == '/' && in->pos + 1 < in->size && in->text[in->pos + 1] == '*' ) + { + int64_t at = in->pos + 2; + while ( at + 1 < in->size && !(in->text[at] == '*' && in->text[at + 1] == '/') ) { at++; } + if ( at + 1 >= in->size ) { in->bad = 1; in->pos = in->size; return; } + in->pos = at + 2; + continue; + } if ( c == '/' ) { in->bad = 1; } return; } @@ -844,26 +1090,19 @@ static SCHEMA_UNUSED int32_t table_json_encode_utf8( uint32_t code, char * unit never cut through a multi-byte character. Clamping is counted, never fatal, exactly as it is on the wire (§4). A NULL destination scans past a string without keeping it. */ -static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +static SCHEMA_UNUSED int table_json_scan_unit( TableJsonIn * in, char * unit, int32_t * unit_length_out ) { - int32_t placed = 0; - int clamped = 0; - if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } - in->pos++; - for ( ;; ) + int32_t unit_length = 0; + *unit_length_out = 0; { - char c; - char unit[4]; - int32_t unit_length = 0; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - c = in->text[in->pos]; - if ( c == '"' ) { in->pos++; break; } + char c = in->text[in->pos]; + if ( c == '"' ) { in->pos++; return 1; } if ( c == '\\' ) { - char escape; in->pos++; if ( in->pos >= in->size ) { in->bad = 1; return 0; } - escape = in->text[in->pos++]; + char escape = in->text[in->pos++]; switch ( escape ) { case '"': unit[0] = '"'; unit_length = 1; break; @@ -877,29 +1116,27 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i case 'u': { int high = table_json_hex4( in ); - uint32_t code; if ( high < 0 ) { in->bad = 1; return 0; } - code = (uint32_t) high; + uint32_t code = (uint32_t) high; if ( high >= 0xd800 && high <= 0xdbff && in->pos + 2 <= in->size && in->text[in->pos] == '\\' && in->text[in->pos + 1] == 'u' ) { int64_t mark = in->pos; - int low; in->pos += 2; - low = table_json_hex4( in ); + int low = table_json_hex4( in ); if ( low >= 0xdc00 && low <= 0xdfff ) { code = 0x10000 + ( ( (uint32_t) high - 0xd800 ) << 10 ) + ( (uint32_t) low - 0xdc00 ); } else { - in->pos = mark; /* a lone lead surrogate rides as itself */ + in->pos = mark; // a lone lead surrogate rides as itself } } - /* a surrogate half that never found its partner has no - UTF-8 encoding: encoding it anyway would manufacture - CESU-8 — invalid UTF-8 — out of input that was valid - JSON, so it reads as the replacement character */ + // a surrogate half that never found its partner has no + // UTF-8 encoding: encoding it anyway would manufacture + // CESU-8 — invalid UTF-8 — out of input that was valid + // JSON, so it reads as the replacement character if ( code >= 0xd800 && code <= 0xdfff ) { code = 0xfffd; } unit_length = table_json_encode_utf8( code, unit ); break; @@ -909,16 +1146,16 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i } else if ( (unsigned char) c < 0x20 ) { - in->bad = 1; /* a raw control character is not a JSON string body */ + in->bad = 1; // a raw control character is not a JSON string body return 0; } else { - /* a UTF-8 sequence read WHOLE, so the clamp below can only land - between code points. Only bytes that ACTUALLY look like - continuations are taken: the wire imposes no encoding (§3), so - a string may legitimately hold a stray lead byte, and one at - the end of a text must not swallow the closing quote. */ + // a UTF-8 sequence read WHOLE, so the clamp below can only land + // between code points. Only bytes that ACTUALLY look like + // continuations are taken: the wire imposes no encoding (§3), so + // a string may legitimately hold a stray lead byte, and one at + // the end of a text must not swallow the closing quote. unsigned char lead = (unsigned char) c; int32_t want = 1; if ( ( lead & 0xe0 ) == 0xc0 ) { want = 2; } @@ -932,10 +1169,43 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i { unit[unit_length++] = in->text[in->pos++]; } + // A SEQUENCE THAT IS NOT A CODE POINT READS AS U+FFFD, which is + // §16.3's rule at the point the defect ENTERS rather than at the + // point it leaves: a lone surrogate escape already reads that way + // above, RFC 8259 requires a JSON text to be valid UTF-8, and a + // kind 12 payload is well-formed UTF-8 (§3), so storage the text + // form built has to be storage the wire can carry (§5). + int32_t checked_width = 0; + if ( table_json_utf8( unit, unit_length, &checked_width ) < 0 ) + { + unit_length = table_json_encode_utf8( 0xfffd, unit ); + } } - if ( out != NULL ) + } + *unit_length_out = unit_length; + return 1; +} + + +/* Scan decoded bytes independently of the field bound: escapes first, UTF-8 + replacement next, then the caller's capacity. Report the used UTF-8 length; + this parser does not treat a byte buffer's contents as a table value. */ +static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, int32_t capacity, int32_t * length ) +{ + int32_t placed = 0; + int clamped = 0; + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + if ( out == NULL ) { placed += unit_length; } + else { - if ( placed + unit_length <= capacity ) + if ( !clamped && placed + unit_length <= capacity ) { memcpy( out + placed, unit, (size_t) unit_length ); placed += unit_length; @@ -951,6 +1221,78 @@ static SCHEMA_UNUSED int table_json_scan_string( TableJsonIn * in, char * out, i return 1; } + +static SCHEMA_UNUSED uint32_t table_json_decode_utf8( const char * unit, int32_t unit_length ) +{ + const unsigned char lead = (unsigned char) unit[0]; + if ( unit_length == 1 ) { return lead; } + uint32_t code = lead & ( unit_length == 2 ? 0x1fu : ( unit_length == 3 ? 0x0fu : 0x07u ) ); + for ( int32_t i = 1; i < unit_length; i++ ) + { + code = ( code << 6 ) | (uint32_t) ( (unsigned char) unit[i] & 0x3f ); + } + return code; +} + +// Scan one JSON string into a caller buffer of UTF-16 CODE UNITS: the wstring +// row of §16.2, the text TRANSCODED at the boundary. It shares +// table_json_scan_unit with the narrow scan, so the escape grammar and the +// lone-surrogate rule are one grammar, and appends ONE CODE POINT AT A TIME — +// one unit below U+10000 and a surrogate PAIR above it. A string longer than +// the field is therefore clamped at N code units with a pair never split, and +// a high surrogate left without its low half is dropped with it, which is the +// same sentence the wire's clamp takes (§3). Clamping is counted, never fatal. +static SCHEMA_UNUSED int table_json_scan_w_string( TableJsonIn * in, uint16_t * out, int32_t capacity, int32_t * length ) +{ + if ( table_json_peek( in ) != '"' ) { in->bad = 1; return 0; } + in->pos++; + int32_t placed = 0; + int clamped = 0; + for ( ;; ) + { + char unit[4]; + int32_t unit_length = 0; + if ( !table_json_scan_unit( in, unit, &unit_length ) ) { return 0; } + if ( unit_length == 0 ) { break; } + const uint32_t code = table_json_decode_utf8( unit, unit_length ); + uint16_t units[2]; + int32_t units_length = 1; + if ( code < 0x10000 ) + { + units[0] = (uint16_t) code; + } + else + { + const uint32_t rest = code - 0x10000; + units[0] = (uint16_t) ( 0xd800 + ( rest >> 10 ) ); + units[1] = (uint16_t) ( 0xdc00 + ( rest & 0x3ff ) ); + units_length = 2; + } + if ( out == NULL ) + { + placed += units_length; + } + else if ( !clamped && placed + units_length <= capacity ) + { + for ( int32_t i = 0; i < units_length; i++ ) { out[placed + i] = units[i]; } + placed += units_length; + } + else + { + // A CLAMP IS A PREFIX, the narrow scan's own rule: once one code + // point does not fit, the scan stops placing. A pair is placed + // whole or not at all, so no clamp can leave an unpaired + // surrogate in storage. + clamped = 1; + } + } + if ( clamped ) { in->report->clamped++; } + if ( length != NULL ) { *length = placed; } + return 1; +} + + + /* Scan one number, to JSON's OWN grammar (RFC 8259 §6) and not to a run of number-ish characters: @@ -1084,24 +1426,41 @@ static SCHEMA_UNUSED int64_t table_json_token_integer( const char * token, int32 return (int64_t) magnitude; } +#ifdef SCHEMA_TABLE_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ); +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ); +#else +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ (void)slot; (void)f; (void)depth; in->bad = 1; return 0; } +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ (void)key; in->bad = 1; return 0; } +#endif + static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth ); static SCHEMA_UNUSED int table_json_skip_container( TableJsonIn * in, char close, int32_t depth ) { if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } + int first = 1; in->pos++; /* the opening bracket */ for ( ;; ) { char c = table_json_peek( in ); if ( c == close ) { in->pos++; return 1; } if ( c == 0 ) { in->bad = 1; return 0; } + int consumed = 0; if ( close == '}' ) { - if ( !table_json_scan_string( in, NULL, 0, NULL ) ) { return 0; } + char key[kTableJsonMaxKey]; + int32_t length = 0; + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } in->pos++; + if ( key[0] == '&' && in->graph != NULL ) { if ( !first ) { in->bad = 1; return 0; } if ( !table_json_skipped_ampersand(in,key) ) { return 0; } consumed = 1; } } - if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } + first = 0; + if ( !consumed && !table_json_skip_value( in, depth + 1 ) ) { return 0; } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; continue; } /* a trailing comma is accepted */ if ( c == close ) { in->pos++; return 1; } @@ -1136,16 +1495,263 @@ static SCHEMA_UNUSED int table_json_skip_value( TableJsonIn * in, int32_t depth } static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ); +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ); + +typedef struct TableJsonInteger +{ + uint64_t magnitude; // |value|, exact for every integral token 64 bits hold + int negative; + int fractional; // a genuinely fractional VALUE: the wrong shape for an integer + int saturated; // a magnitude past what 64 bits hold, held at that edge + int finite; // 0: no integer target holds it at all +} TableJsonInteger; + +// THE FIELD'S INTERPRETATION: the token, parsed digit by digit so no width and +// no locale can move it, and through the runtime's converter only where the +// spelling carries a fraction or an exponent +static SCHEMA_UNUSED TableJsonInteger table_json_interpret( const char * token, int32_t length, int integral ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + if ( integral ) + { + int32_t i = 0; + if ( i < length && token[i] == '-' ) // WalkNumber refuses a leading plus + { + out.negative = 1; + i++; + } + for ( ; i < length; i++ ) + { + const uint64_t digit = (uint64_t) ( token[i] - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + break; + } + out.magnitude = out.magnitude * 10 + digit; + } + if ( out.magnitude == 0 ) { out.negative = 0; } // -0 IS zero + return out; + } + const double d = table_json_token_double( token, length, 0 ); + if ( !table_json_finite( d ) ) { out.finite = 0; return out; } + out.negative = d < 0; + const double whole = out.negative ? -d : d; + // THE DOMAIN IS ESTABLISHED BEFORE THE CAST: a magnitude past what sixty-four + // bits hold is answered here, so no value ever reaches a conversion that is + // undefined for it + if ( whole >= 18446744073709551616.0 ) + { + out.magnitude = UINT64_MAX; + out.saturated = 1; + return out; + } + const uint64_t truncated = (uint64_t) whole; + if ( (double) truncated != whole ) { out.fractional = 1; return out; } + out.magnitude = truncated; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +static SCHEMA_UNUSED TableJsonInteger table_json_integer_of( double bound ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = bound < 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + const double whole = out.negative ? -bound : bound; + out.magnitude = whole >= 18446744073709551616.0 ? UINT64_MAX : (uint64_t) whole; + if ( out.magnitude == 0 ) { out.negative = 0; } + return out; +} + +// THE TARGET DOMAIN, established before the value reaches storage: the bytes of +// storage, signed or not. Answers what the target holds and whether the domain +// MOVED it. An unsigned magnitude above INT64_MAX rides out as its bit pattern, +// which is the storage's own image of it and not a negative number. +static SCHEMA_UNUSED int64_t table_json_integer_in_domain( TableJsonInteger number, int is_signed, int32_t bytes, int * moved ) +{ + *moved = 0; + uint64_t magnitude = number.magnitude; + if ( is_signed ) + { + const uint64_t high = bytes >= 8 ? (uint64_t) INT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 - 1 ) ) - 1 ); + if ( number.negative ) + { + const uint64_t low = high + 1; // the floor's magnitude + if ( magnitude > low ) { magnitude = low; *moved = 1; } + return (int64_t) ( ~magnitude + 1 ); // two's complement, INT64_MIN included + } + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; + } + // A NEGATIVE TOKEN IN AN UNSIGNED FIELD CLAMPS TO ZERO, and -0 is zero, + // which is why the sign is dropped at a zero magnitude above + if ( number.negative ) { *moved = 1; return 0; } + const uint64_t high = bytes >= 8 ? UINT64_MAX : ( ( (uint64_t) 1 << ( bytes * 8 ) ) - 1 ); + if ( magnitude > high ) { magnitude = high; *moved = 1; } + return (int64_t) magnitude; +} + + +static SCHEMA_UNUSED int table_json_read_wide( TableJsonIn * in, const char * token, int32_t length, void * storage, const TableFieldInfo * f ) +{ + int is_signed = table_json_kind_wide_signed( f->kind ); + int frac = f->frac_bits; + int32_t i = 0; + int negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { negative = token[i] == '-'; i++; } + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // the digits, with the point after "point" of them; leading and trailing + // zeros stripped. digit( k ) reads the k-th of the int and frac runs. + int32_t start = 0, end = int_len + frac_len; + int64_t point = int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + + TableJsonWide raw = { 0, 0 }; + int saturated = 0; + TableJsonWide signed_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) >> 1 }; + TableJsonWide signed_min = { 0, (uint64_t) ( 1 ) << 63 }; + TableJsonWide unsigned_max = { ~(uint64_t) ( 0 ), ~(uint64_t) ( 0 ) }; + if ( start == end ) + { + // zero, and -0 IS zero + } + else if ( point > 40 ) + { + saturated = 1; + if ( !negative ) { raw = is_signed ? signed_max : unsigned_max; } + else if ( is_signed ) { raw = signed_min; } + } + else if ( point < -40 ) + { + in->report->kind_mismatch++; // finer than any F can spell + return 1; + } + else + { + // the fraction FIRST, so an inexact value is the wrong shape whatever + // its magnitude: its digits, with the zeros a negative point puts in + // front, doubled F times; each doubling's carry is the next bit, and + // the value is exact iff nothing is left after the last one + char fd[kTableJsonMaxNumber + 48]; + int32_t fn = 0; + for ( int64_t z = point; z < 0; z++ ) { fd[fn++] = 0; } + for ( int32_t k = (int32_t) ( point > 0 ? point : 0 ) + start; k < end; k++ ) + { + fd[fn++] = (char) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + } + TableJsonWide fraction = { 0, 0 }; + for ( int b = 0; b < frac; b++ ) + { + int carry = 0; + for ( int32_t k = fn - 1; k >= 0; k-- ) + { + int d = fd[k] * 2 + carry; + fd[k] = (char) ( d % 10 ); + carry = d / 10; + } + fraction = table_json_wide_shl( fraction, 1 ); + fraction.lo |= (uint64_t) carry; + } + for ( int32_t k = 0; k < fn; k++ ) + { + if ( fd[k] != 0 ) + { + in->report->kind_mismatch++; + return 1; + } + } + // then the whole part, saturating past 128 bits + TableJsonWide whole = { 0, 0 }; + for ( int64_t k = start; k < start + point && !saturated; k++ ) + { + uint32_t digit = k < end ? (uint32_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ) : 0; + if ( table_json_wide_mul_add( &whole, 10, digit ) != 0 ) { saturated = 1; } + } + if ( !saturated && frac > 0 && !table_json_wide_zero( table_json_wide_shr( whole, 128 - frac ) ) ) { saturated = 1; } + if ( !saturated ) + { + raw = table_json_wide_shl( whole, frac ); + raw.lo |= fraction.lo; + raw.hi |= fraction.hi; + } + if ( is_signed ) + { + if ( !saturated && !negative && table_json_wide_negative( raw ) ) { saturated = 1; } + if ( !saturated && negative && table_json_wide_compare( raw, signed_min, 0 ) > 0 ) { saturated = 1; } + if ( saturated ) { raw = negative ? signed_min : signed_max; } + else if ( negative ) { raw = table_json_wide_neg( raw ); } + } + else + { + if ( saturated ) { raw = unsigned_max; } + if ( negative && !table_json_wide_zero( raw ) ) { raw.lo = 0; raw.hi = 0; saturated = 1; } + } + } + if ( saturated ) { in->report->clamped++; } + if ( f->wide != NULL ) + { + TableJsonWide lo = { f->wide->lo[0], f->wide->lo[1] }; + TableJsonWide hi = { f->wide->hi[0], f->wide->hi[1] }; + if ( table_json_wide_compare( raw, lo, is_signed ) < 0 ) { raw = lo; in->report->clamped++; } + else if ( table_json_wide_compare( raw, hi, is_signed ) > 0 ) { raw = hi; in->report->clamped++; } + } + table_json_wide_store( storage, f->elem_size, raw ); + return 1; +} + /* place one scalar at one storage address */ +static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ); +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth); +#endif +#endif static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storage, const TableFieldInfo * f, int32_t depth ) { char token[kTableJsonMaxNumber]; int32_t length = 0; int integral = 0; - int is_signed; - int saturated = 0; - int64_t value = 0; + if ( f->kind == 17 ) + { + if ( table_json_value_shape(in) == 'z' ) { table_json_set_raw(storage,f->elem_size,0); return table_json_literal(in,"null"); } + return table_json_read_pointer(in,storage,f,depth); + } if ( f->arms != NULL ) { /* a union is an object with ONE key, the arm's name; {} is None, and @@ -1177,9 +1783,21 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag else { void * payload = (uint8_t *) storage + arms->arms[tag].offset; - arms->arms[tag].table->reset( payload ); - if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth + 1 ) ) { return 0; } - table_json_set_raw( (uint8_t *) storage + arms->tag_offset, arms->tag_size, (uint64_t) tag ); + const TableFieldInfo * arm = arms->arms[tag].field; + char want = arm != NULL ? table_json_shape( arm ) : (arms->arms[tag].table != NULL ? 'o' : 'z'); + if ( table_json_value_shape( in ) != want && !(arm != NULL && arm->kind == 17 && !arm->is_array && table_json_value_shape(in) == 'z') ) { + in->report->kind_mismatch++; + if ( !table_json_skip_value( in, depth+1 ) ) { return 0; } + } else { + if ( arm != NULL ) { + memset( payload, 0, arms->arms[tag].size ); + if ( !table_json_read_field( in, storage, arm, depth+1 ) ) { return 0; } + } else if ( arms->arms[tag].table != NULL ) { + arms->arms[tag].table->reset( payload ); + if ( !table_json_read_table( in, payload, arms->arms[tag].table, depth+1 ) ) { return 0; } + } else if ( !table_json_literal( in, "null" ) ) { return 0; } + table_json_set_raw( (uint8_t *)storage+arms->tag_offset, arms->tag_size, (uint64_t)tag ); + } } c = table_json_peek( in ); if ( c == ',' ) { in->pos++; c = table_json_peek( in ); } @@ -1271,6 +1889,7 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag in->bad = 1; return 0; } + if ( table_json_kind_wide( f->kind ) ) { return table_json_read_wide( in, token, length, storage, f ); } if ( f->kind == 10 || f->kind == 11 ) { int single = f->kind == 10; @@ -1308,74 +1927,33 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag } return 1; } - /* JSON HAS ONE NUMBER TYPE. 2.0 IS the integer 2 and 1e3 IS 1000, and a - library that round-trips numbers through a double emits them that way — - this walker's own float writer emits 1e+21. So an integer field takes - any number whose VALUE is integral, however it was spelled; only a - genuinely fractional value is the wrong shape for it. */ - is_signed = f->kind >= 2 && f->kind <= 5; - if ( integral ) + // AN ORDINARY FIELD'S POLICY over the one interpreted value: it CLAMPS to + // its domain and counts. JSON has one number type, so 2.0 IS the integer 2 + // and 1e3 IS 1000. A library that round-trips numbers through a double + // emits them that way, and this walker's own float writer emits 1e+21. Only + // a genuinely fractional value is the wrong shape for the kind. + const int is_signed = f->kind >= 2 && f->kind <= 5; + const TableJsonInteger number = table_json_interpret( token, length, integral ); + if ( !number.finite || number.fractional ) { - value = table_json_token_integer( token, length, is_signed, &saturated ); - } - else - { - double d = table_json_token_double( token, length, 0 ); - if ( !table_json_finite( d ) ) - { - in->report->kind_mismatch++; - return 1; - } - if ( is_signed ) - { - if ( d >= 9223372036854775808.0 ) { value = INT64_MAX; saturated = 1; } - else if ( d < -9223372036854775808.0 ) { value = INT64_MIN; saturated = 1; } - else if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) d; } - } - else - { - if ( d < 0.0 ) - { - /* a negative for an unsigned field clamps to zero, as the - exact digit path already does */ - if ( d != (double) (int64_t) d ) { in->report->kind_mismatch++; return 1; } - value = 0; - saturated = 1; - } - else if ( d >= 18446744073709551616.0 ) { value = (int64_t) UINT64_MAX; saturated = 1; } - else if ( d != (double) (uint64_t) d ) { in->report->kind_mismatch++; return 1; } - else { value = (int64_t) (uint64_t) d; } - } + in->report->kind_mismatch++; + return 1; } - if ( saturated ) { in->report->clamped++; } + if ( number.saturated ) { in->report->clamped++; } // past what sixty-four bits hold + // THE DECLARED RANGE FIRST, THEN THE STORAGE WIDTH, the wire's order (§4), + // so a text and a wire loaded from the same data land the same instance. + // The comparison is on the value's OWN scale, correctly signed past + // INT64_MAX, where the storage's bit pattern is not a number to compare. + TableJsonInteger bounded = number; if ( f->has_range ) { - if ( (double) value < f->range_min ) { value = (int64_t) f->range_min; in->report->clamped++; } - else if ( (double) value > f->range_max ) { value = (int64_t) f->range_max; in->report->clamped++; } - } - /* the field's own storage width is the last bound: a value past it - clamps rather than wrapping, which is what the wire does too */ - if ( f->elem_size < 8 ) - { - if ( is_signed ) - { - int64_t high = ( (int64_t) 1 << ( f->elem_size * 8 - 1 ) ) - 1; - int64_t low = -high - 1; - if ( value > high ) { value = high; in->report->clamped++; } - else if ( value < low ) { value = low; in->report->clamped++; } - } - else - { - uint64_t high = ( (uint64_t) 1 << ( f->elem_size * 8 ) ) - 1; - if ( value < 0 ) { value = 0; in->report->clamped++; } - else if ( (uint64_t) value > high ) { value = (int64_t) high; in->report->clamped++; } - } + const double scale = number.negative ? -(double) number.magnitude : (double) number.magnitude; + if ( scale < f->range_min ) { bounded = table_json_integer_of( f->range_min ); in->report->clamped++; } + else if ( scale > f->range_max ) { bounded = table_json_integer_of( f->range_max ); in->report->clamped++; } } - /* at eight bytes the storage IS the parser's width, and an unsigned value - past INT64_MAX rides here as a negative int64 by design — the token - parser already turned a NEGATIVE token for an unsigned field into a - clamped zero, so there is nothing left to bound. */ + int moved = 0; + const int64_t value = table_json_integer_in_domain( bounded, is_signed, (int32_t) f->elem_size, &moved ); + if ( moved ) { in->report->clamped++; } table_json_set_raw( storage, f->elem_size, (uint64_t) value ); return 1; } @@ -1383,6 +1961,18 @@ static SCHEMA_UNUSED int table_json_read_scalar( TableJsonIn * in, void * storag static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, const TableFieldInfo * f, int32_t depth ) { uint8_t * storage = (uint8_t *) base + f->offset; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + if(f->sequence==1) { return table_json_read_list(in,storage,f,depth); } +#ifdef SCHEMA_TABLE_MAP_RUNTIME + if(f->sequence==2) { return table_json_read_map(in,storage,f,depth); } +#endif +#endif + if ( f->kind == 33 ) { + int32_t length = 0; + if ( !table_json_scan_w_string( in, (uint16_t *)(void *) storage, f->array_bound, &length ) ) { return 0; } + ((uint16_t *)(void *)storage)[length] = 0; + table_json_set_count( base, f, length ); return 1; + } if ( f->kind == 12 ) { int32_t length = 0; @@ -1498,7 +2088,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->unknown++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1553,7 +2143,7 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c in->report->clamped++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } } - else if ( table_json_value_shape( in ) != shape ) + else if ( table_json_value_shape( in ) != shape && !(f->kind == 17 && table_json_value_shape(in) == 'z') ) { in->report->kind_mismatch++; if ( !table_json_skip_value( in, depth + 1 ) ) { return 0; } @@ -1582,28 +2172,30 @@ static SCHEMA_UNUSED int table_json_read_field( TableJsonIn * in, void * base, c counted, a repeated key is last-wins and counted. The instance is already at its declared defaults when this is entered, so a key the text never mentions keeps the default an absent field takes on the wire (§4). */ -static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +static SCHEMA_UNUSED int table_json_read_table_keys( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth, const char * first_key ) { /* duplicate tracking, bounded and allocation-free: a table with more fields than this still reads, its repeats simply stop being counted */ uint64_t seen[8]; - if ( depth > kTableJsonMaxDepth ) { in->bad = 1; return 0; } - if ( table_json_peek( in ) != '{' ) { in->bad = 1; return 0; } - in->pos++; memset( seen, 0, sizeof( seen ) ); for ( ;; ) { - char c = table_json_peek( in ); + char c = 0; char key[kTableJsonMaxKey]; int32_t key_length = 0; int32_t index = -1; int32_t i; - if ( c == '}' ) { in->pos++; return 1; } - if ( c == 0 ) { in->bad = 1; return 0; } - if ( !table_json_scan_string( in, key, kTableJsonMaxKey - 1, &key_length ) ) { return 0; } - key[key_length] = 0; - if ( table_json_peek( in ) != ':' ) { in->bad = 1; return 0; } - in->pos++; + if ( first_key != NULL ) { memcpy(key,first_key,strlen(first_key)+1); first_key = NULL; } + else + { + c = table_json_peek(in); + if ( c == '}' ) { in->pos++; return 1; } + if ( c == 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&key_length) ) { return 0; } + key[key_length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + } for ( i = 0; i < info->num_fields; i++ ) { if ( strcmp( info->fields[i].json, key ) == 0 ) { index = i; break; } @@ -1648,7 +2240,7 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c } else { - if ( got != table_json_shape( f ) ) + if ( got != table_json_shape( f ) && !(f->kind == 17 && !f->is_array && got == 'z') ) { /* the wrong JSON type for the kind: skipped, never coerced */ in->report->kind_mismatch++; @@ -1674,6 +2266,13 @@ static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, c /* ---- the two entry points the per-table wrappers name ---- */ +static SCHEMA_UNUSED int table_json_read_table( TableJsonIn * in, void * base, const TableTypeInfo * info, int32_t depth ) +{ + if ( depth > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; + return table_json_read_table_keys(in,base,info,depth,NULL); +} + static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) { TableReport ignored; @@ -1685,6 +2284,7 @@ static SCHEMA_UNUSED int table_json_read( void * value, const TableTypeInfo * in in.pos = 0; in.report = report != NULL ? report : &ignored; in.bad = 0; + in.graph = NULL; info->reset( value ); if ( text == NULL || bytes < 0 ) { @@ -1712,6 +2312,7 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy out.capacity = capacity; out.offset = 0; out.overflow = 0; + out.graph = NULL; if ( !table_json_write_value( &out, value, info, 0 ) ) { return -1; } /* THE CANONICAL TEXT ENDS WITH EXACTLY ONE NEWLINE (docs/SPEC-TABLES.md §16.1). Every writer emits it — this walk, the reference's and @@ -1728,17 +2329,463 @@ static SCHEMA_UNUSED int64_t table_json_write( const void * value, const TableTy /* ---- json walk: end ---- */ + +/* ---- json graph walk: begin ---- */ +typedef struct TableJsonGraphEntry +{ + uint64_t key; + int64_t count, label; + int open; + uint32_t node; + const TableTypeInfo * type; +} TableJsonGraphEntry; + +typedef struct TableJsonGraphMap +{ + TableJsonGraphEntry * entries; + uint64_t capacity, count; + TableAllocator allocator; +} TableJsonGraphMap; + +static SCHEMA_UNUSED uint64_t table_json_graph_slot( const TableJsonGraphMap * map, uint64_t key ) +{ + uint64_t hash = key * UINT64_C(0x9E3779B97F4A7C15); + uint64_t at; + hash ^= hash >> 29; at = hash & (map->capacity-1); + while ( map->entries[at].key != 0 && map->entries[at].key != key ) { at = (at+1) & (map->capacity-1); } + return at; +} + +static SCHEMA_UNUSED TableJsonGraphEntry * table_json_graph_find( TableJsonGraphMap * map, uint64_t key ) +{ + TableJsonGraphEntry * entry; + if ( map->capacity == 0 ) { return NULL; } + entry = &map->entries[table_json_graph_slot(map,key)]; + return entry->key == key ? entry : NULL; +} + +static SCHEMA_UNUSED TableJsonGraphEntry * table_json_graph_reach( TableJsonGraphMap * map, uint64_t key, int * taken ) +{ + TableJsonGraphEntry * entry; + if ( map->capacity == 0 || map->count >= map->capacity - map->capacity/4 - 1 ) + { + TableJsonGraphMap grown; + uint64_t i; + grown.capacity = map->capacity ? map->capacity*4 : 64; + if ( grown.capacity < map->capacity || grown.capacity > INT64_MAX / sizeof(TableJsonGraphEntry) ) { return NULL; } + grown.count = map->count; grown.allocator=map->allocator; + grown.entries = (TableJsonGraphEntry *)table_allocate(map->allocator,(int64_t)grown.capacity*sizeof(TableJsonGraphEntry)); + if ( grown.entries == NULL ) { return NULL; } + for ( i = 0; i < map->capacity; i++ ) + { + if ( map->entries[i].key ) { grown.entries[table_json_graph_slot(&grown,map->entries[i].key)] = map->entries[i]; } + } + table_release(map->allocator,map->entries); *map = grown; + } + entry = &map->entries[table_json_graph_slot(map,key)]; + *taken = entry->key != key; + if ( *taken ) { entry->key = key; map->count++; } + return entry; +} + +typedef struct TableJsonGraphIn +{ + TableWorker * worker; + TableJsonGraphMap labels; +} TableJsonGraphIn; + +static SCHEMA_UNUSED int table_json_scan_label( TableJsonIn * in, uint64_t * label ) +{ + uint64_t value = 0; + table_json_space(in); + if ( in->pos >= in->size || in->text[in->pos] < '1' || in->text[in->pos] > '9' ) { in->bad = 1; return 0; } + while ( in->pos < in->size && in->text[in->pos] >= '0' && in->text[in->pos] <= '9' ) + { + uint64_t digit = (uint64_t)(in->text[in->pos]-'0'); + if ( value > (UINT64_MAX-digit)/10 ) { in->bad = 1; return 0; } + value = value*10+digit; in->pos++; + } + *label = value; return 1; +} + +static SCHEMA_UNUSED void * table_json_emplace( TableJsonGraphIn * graph, void * slot, const TableTypeInfo * type ) +{ + uint32_t at = table_worker_bump(graph->worker,type->size); + void * node; + if ( at == kTableAllocFailed ) { return NULL; } + node = table_arena_at(graph->worker->arena,at); + type->reset(node); ((TableRef *)slot)->value = at; return node; +} + +static SCHEMA_UNUSED int table_json_read_blob( TableJsonIn * in, void * slot, const TableFieldInfo * f ) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; + TableRef * ref=(TableRef *)slot; + int64_t mark, symbols=0, length, placed=0, at; + int malformed=0, held=0; + uint32_t accumulator=0; + uint8_t * data; + if(graph==NULL || table_json_peek(in)!='"') { in->bad=1; return 0; } + ref->value=0; + if(strcmp(f->type_name,"string")==0) + { + int32_t count=0, written=0; + char * text; + mark=in->pos; + if(!table_json_scan_string(in,NULL,0,&count)) { return 0; } + in->pos=mark; text=table_string_emplace(graph->worker,ref,NULL,count); + if(text==NULL) { in->bad=1; return 0; } + return table_json_scan_string(in,text,count,&written); + } + mark=++in->pos; + for(;;) + { + char c; + if(in->pos>=in->size) { in->bad=1; return 0; } + c=in->text[in->pos++]; + if(c=='"') { break; } + if(c=='=' || malformed) { continue; } + if(table_json_base64_value((uint8_t)c)<0) { malformed=1; continue; } + symbols++; + } + if(malformed) { in->report->kind_mismatch++; return 1; } + length=symbols*6/8; + data=table_bytes_emplace(graph->worker,ref,length); + if(data==NULL) { in->bad=1; return 0; } + for(at=mark; in->text[at]!='"'; at++) + { + int32_t symbol=table_json_base64_value((uint8_t)in->text[at]); + if(symbol<0) { continue; } + accumulator=(accumulator<<6)|(uint32_t)symbol; held+=6; + if(held>=8) { held-=8; if(placed>held)&0xff); } } + } + return 1; +} + +static SCHEMA_UNUSED int table_json_read_pointer( TableJsonIn * in, void * slot, const TableFieldInfo * f, int32_t depth ) +{ + TableJsonGraphIn * graph = (TableJsonGraphIn *)in->graph; + TableJsonGraphEntry * entry; + char key[kTableJsonMaxKey], c; + int32_t length = 0; + uint64_t label = 0; + int taken = 0, bare; + void * node; + if(graph!=NULL && f->table==NULL) { return table_json_read_blob(in,slot,f); } + if ( graph == NULL || depth+1 > kTableJsonMaxDepth || table_json_peek(in) != '{' ) { in->bad = 1; return 0; } + in->pos++; c = table_json_peek(in); + if ( c == '}' ) + { + in->pos++; + if ( table_json_emplace(graph,slot,f->table) == NULL ) { in->bad = 1; return 0; } + return 1; + } + if ( !table_json_scan_string(in,key,kTableJsonMaxKey-1,&length) ) { return 0; } + key[length] = 0; + if ( table_json_peek(in) != ':' ) { in->bad = 1; return 0; } + in->pos++; + if ( strcmp(key,"&node") != 0 ) + { + node = table_json_emplace(graph,slot,f->table); + if ( node == NULL ) { in->bad = 1; return 0; } + return table_json_read_table_keys(in,node,f->table,depth+1,key); + } + if ( !table_json_scan_label(in,&label) ) { return 0; } + entry = table_json_graph_reach(&graph->labels,label,&taken); + if ( entry == NULL ) { in->bad = 1; return 0; } + c = table_json_peek(in); + if ( c == ',' ) { in->pos++; c = table_json_peek(in); } + bare = c == '}'; + if ( bare == taken ) { in->bad = 1; return 0; } + if ( bare ) + { + in->pos++; + if ( entry->open ) { in->bad = 1; return 0; } + ((TableRef *)slot)->value = 0; + if ( entry->type == NULL ) { return 1; } + if ( entry->type != f->table ) { in->report->kind_mismatch++; return 1; } + ((TableRef *)slot)->value = entry->node; return 1; + } + node = table_json_emplace(graph,slot,f->table); + if ( node == NULL ) { in->bad = 1; return 0; } + entry->open = 1; + if ( !table_json_read_table_keys(in,node,f->table,depth+1,NULL) ) { return 0; } + entry = table_json_graph_find(&graph->labels,label); + if ( entry == NULL ) { in->bad = 1; return 0; } + entry->node = (uint32_t)((TableRef *)slot)->value; entry->type = f->table; entry->open = 0; return 1; +} + +static SCHEMA_UNUSED int table_json_skipped_ampersand( TableJsonIn * in, const char * key ) +{ + TableJsonGraphIn * graph = (TableJsonGraphIn *)in->graph; + uint64_t label = 0; + int taken = 0; + if ( graph == NULL || strcmp(key,"&node") != 0 ) { in->bad = 1; return 0; } + if ( !table_json_scan_label(in,&label) ) { return 0; } + if ( table_json_graph_reach(&graph->labels,label,&taken) == NULL ) { in->bad = 1; return 0; } + return 1; +} + +typedef struct TableJsonGraphOut +{ + TableJsonGraphMap nodes; + int counting; + int64_t next_label; +} TableJsonGraphOut; + +static SCHEMA_UNUSED int table_json_write_pointer( TableJsonOut * out, const void * slot, const TableFieldInfo * f, int32_t depth ) +{ + TableJsonGraphOut * graph = (TableJsonGraphOut *)out->graph; + const void * node = table_ref_at(NULL,(const TableRef *)slot); + TableJsonGraphEntry * entry; + int taken = 0, any = 1; + int64_t before; + if ( graph == NULL ) { return 0; } + if ( node == NULL ) { table_json_raw(out,"null",4); return 1; } + entry = table_json_graph_reach(&graph->nodes,(uint64_t)(uintptr_t)node,&taken); + if ( entry == NULL ) { return 0; } + if(f->table==NULL) + { + const TableBlob * blob=(const TableBlob *)node; + if(graph->counting) { entry->count++; return 1; } + if(entry->count>1 || blob->length>INT32_MAX) { return 0; } + if(strcmp(f->type_name,"string")==0) { table_json_write_string(out,(const char *)(blob+1),(int32_t)blob->length); } + else { table_json_write_base64(out,(const uint8_t *)(blob+1),(int32_t)blob->length); } + return 1; + } + if ( graph->counting ) + { + entry->count++; + if ( !taken ) { return entry->open == 0; } + entry->open = 1; + if ( !table_json_write_value(out,node,f->table,depth) ) { return 0; } + entry = table_json_graph_find(&graph->nodes,(uint64_t)(uintptr_t)node); + if ( entry == NULL ) { return 0; } + entry->open = 0; return 1; + } + if ( entry->count <= 1 ) { return table_json_write_value(out,node,f->table,depth); } + if ( depth > kTableJsonMaxDepth ) { return 0; } + table_json_put(out,'{'); table_json_line(out,depth+1); table_json_raw(out,"\"&node\": ",9); + if ( entry->label ) + { + table_json_write_unsigned(out,(uint64_t)entry->label); + table_json_line(out,depth); table_json_put(out,'}'); return 1; + } + entry->label = ++graph->next_label; + table_json_write_unsigned(out,(uint64_t)entry->label); + before = out->offset; + if ( !table_json_write_fields(out,node,f->table,depth,&any) || out->offset == before ) { return 0; } + table_json_line(out,depth); table_json_put(out,'}'); return 1; +} + +static SCHEMA_UNUSED int table_json_read_graph( TableWorker * worker, void * root, const TableTypeInfo * info, const char * text, int64_t bytes, TableReport * report ) +{ + TableJsonGraphIn graph; + TableJsonIn in; + TableReport ignored = {0}; + int ok; + memset(&graph,0,sizeof(graph)); graph.worker = worker; graph.labels.allocator=worker->arena!=NULL ? worker->arena->allocator : table_default_allocator(); + memset(&in,0,sizeof(in)); in.text = text; in.size = bytes; in.report = report ? report : &ignored; in.graph = &graph; + if ( root == NULL || worker->arena == NULL || text == NULL || bytes < 0 ) { in.report->malformed = 1; return 0; } + info->reset(root); + ok = table_json_read_table(&in,root,info,0); + if ( ok ) { table_json_space(&in); if ( in.pos != in.size ) { in.bad = 1; } } + table_release(graph.labels.allocator,graph.labels.entries); + if ( in.bad || !ok ) { in.report->malformed = 1; return 0; } + return 1; +} + +static SCHEMA_UNUSED int64_t table_json_write_graph( const void * root, const TableTypeInfo * info, char * buffer, int64_t capacity, TableAllocator allocator ) +{ + TableJsonGraphOut graph; + TableJsonOut count, out; + TableJsonGraphEntry * entry; + int taken = 0, ok; + if ( root == NULL ) { return -1; } + memset(&graph,0,sizeof(graph)); graph.counting = 1; graph.nodes.allocator=allocator; + entry = table_json_graph_reach(&graph.nodes,(uint64_t)(uintptr_t)root,&taken); + if ( entry == NULL ) { return -1; } + entry->open = 1; + memset(&count,0,sizeof(count)); count.graph = &graph; + ok = table_json_write_value(&count,root,info,0); + graph.counting = 0; + memset(&out,0,sizeof(out)); out.buffer = buffer; out.capacity = capacity; out.graph = &graph; + if ( ok ) { ok = table_json_write_value(&out,root,info,0); } + table_release(graph.nodes.allocator,graph.nodes.entries); + if ( !ok ) { return -1; } + table_json_put(&out,'\n'); return out.overflow ? -1 : out.offset; +} +/* ---- json graph walk: end ---- */ + +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME +static SCHEMA_UNUSED int table_json_write_list(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth) +{ + const TableSequence * list=(const TableSequence *)storage; + TableSequenceCursor cursor=table_sequence_cursor(NULL,list,f->elem_size); int32_t i; + if(!cursor.ok) { return 0; } + table_json_put(out,'['); + for(i=0;icount;i++) { + const void * element=table_sequence_next(&cursor); if(element==NULL) { return 0; } + if(i) { table_json_put(out,','); } table_json_line(out,depth+1); + if(!table_json_write_scalar(out,element,f,depth+1)) { return 0; } + } + if(list->count) { table_json_line(out,depth); } table_json_put(out,']'); return 1; +} +static SCHEMA_UNUSED int table_json_read_list(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; + TableSequence * list=(TableSequence *)storage; char shape=table_json_element_shape(f); + if(graph==NULL || table_json_peek(in)!='[') { in->bad=1; return 0; } + memset(list,0,sizeof(*list)); in->pos++; + for(;;) { + char c=table_json_peek(in); void * element; + if(c==']') { in->pos++; return 1; } + if(c==0) { in->bad=1; return 0; } + element=table_sequence_add(graph->worker,list,f->elem_size); + if(element==NULL) { in->bad=1; return 0; } + if(f->kind==13) { f->table->reset(element); } + if(table_json_value_shape(in)!=shape && !(f->kind==17 && table_json_value_shape(in)=='z')) { + in->report->kind_mismatch++; if(!table_json_skip_value(in,depth+1)) { return 0; } + } else if(!table_json_read_scalar(in,element,f,depth+1)) { return 0; } + c=table_json_peek(in); + if(c==',') { in->pos++; continue; } + if(c==']') { in->pos++; return 1; } + in->bad=1; return 0; + } +} +#endif + +#ifdef SCHEMA_TABLE_MAP_RUNTIME +static SCHEMA_UNUSED TableJsonInteger table_json_interpret_exact( const char * token, int32_t length ) +{ + TableJsonInteger out; + out.magnitude = 0; + out.negative = 0; + out.fractional = 0; + out.saturated = 0; + out.finite = 1; + int32_t i = 0; + if ( i < length && token[i] == '-' ) { out.negative = 1; i++; } // WalkNumber refuses a leading plus + const char * int_digits = token + i; + int32_t int_len = 0; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { int_len++; i++; } + const char * frac_digits = token + i; + int32_t frac_len = 0; + if ( i < length && token[i] == '.' ) + { + i++; + frac_digits = token + i; + while ( i < length && token[i] >= '0' && token[i] <= '9' ) { frac_len++; i++; } + } + int64_t exp = 0; + if ( i < length && ( token[i] == 'e' || token[i] == 'E' ) ) + { + i++; + int exp_negative = 0; + if ( i < length && ( token[i] == '-' || token[i] == '+' ) ) { exp_negative = token[i] == '-'; i++; } + while ( i < length && token[i] >= '0' && token[i] <= '9' ) + { + if ( exp < 100000 ) { exp = exp * 10 + ( token[i] - '0' ); } + i++; + } + if ( exp_negative ) { exp = -exp; } + } + // leading and trailing zeros stripped, so the last digit kept is significant + int32_t start = 0, end = int_len + frac_len; + int64_t point = (int64_t) int_len + exp; + while ( start < end && ( start < int_len ? int_digits[start] : frac_digits[start - int_len] ) == '0' ) { start++; point--; } + while ( end > start && ( end - 1 < int_len ? int_digits[end - 1] : frac_digits[end - 1 - int_len] ) == '0' ) { end--; } + const int64_t digits = end - start; + if ( digits == 0 ) { out.negative = 0; return out; } // the value is zero, and -0 IS zero + if ( point < digits ) { out.fractional = 1; return out; } // a significant digit below the point + if ( point > 20 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + for ( int32_t k = start; k < end; k++ ) + { + const uint64_t digit = (uint64_t) ( ( k < int_len ? int_digits[k] : frac_digits[k - int_len] ) - '0' ); + if ( out.magnitude > ( UINT64_MAX - digit ) / 10 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + out.magnitude = out.magnitude * 10 + digit; + } + for ( int64_t k = digits; k < point; k++ ) // the point's own zeros, which no digit spells + { + if ( out.magnitude > UINT64_MAX / 10 ) { out.magnitude = UINT64_MAX; out.saturated = 1; return out; } + out.magnitude *= 10; + } + return out; +} + +static SCHEMA_UNUSED int table_json_write_map(TableJsonOut * out,const void * storage,const TableFieldInfo * f,int32_t depth) +{ + const TableSequence * map=(const TableSequence *)storage; + TableSequenceCursor cursor=table_sequence_cursor(NULL,map,f->elem_size); int32_t i; + const TableFieldInfo * key=&f->table->fields[0]; const TableFieldInfo * value=&f->table->fields[1]; + if(!cursor.ok) { return 0; } table_json_put(out,'{'); + for(i=0;icount;i++) { + const uint8_t * entry=(const uint8_t *)table_sequence_next(&cursor); if(entry==NULL) { return 0; } + if(i) { table_json_put(out,','); } table_json_line(out,depth+1); + if(key->kind==12) { table_json_write_string(out,(const char *)entry+key->offset,table_json_count(entry,key)); } + else { + table_json_put(out,'"'); + if(key->kind>=2 && key->kind<=5) { table_json_write_signed(out,table_json_get_signed(entry+key->offset,key->elem_size)); } + else { table_json_write_unsigned(out,table_json_get_raw(entry+key->offset,key->elem_size)); } + table_json_put(out,'"'); + } + table_json_raw(out,": ",2); if(!table_json_write_field(out,entry,value,depth+1)) { return 0; } + } + if(map->count) { table_json_line(out,depth); } table_json_put(out,'}'); return 1; +} +static SCHEMA_UNUSED int table_json_map_key_value(const char * token,int32_t length,const TableFieldInfo * key,int64_t * value,int * fits) +{ + TableReport scratch={0}; TableJsonIn probe; TableJsonInteger number; int integral=0,moved=0; + memset(&probe,0,sizeof(probe)); probe.text=token; probe.size=length; probe.report=&scratch; *fits=0; + table_json_space(&probe); if(probe.pos!=0 || !table_json_walk_number(&probe,&integral) || probe.pos!=length) { return 0; } + number=table_json_interpret_exact(token,length); if(number.fractional || number.saturated) { return 1; } + *value=table_json_integer_in_domain(number,key->kind>=2 && key->kind<=5,(int32_t)key->elem_size,&moved); *fits=!moved; return 1; +} +static SCHEMA_UNUSED int table_json_read_map(TableJsonIn * in,void * storage,const TableFieldInfo * f,int32_t depth) +{ + TableJsonGraphIn * graph=(TableJsonGraphIn *)in->graph; TableSequence * map=(TableSequence *)storage; + const TableFieldInfo * key=&f->table->fields[0]; const TableFieldInfo * value=&f->table->fields[1]; char shape=table_json_shape(value); + if(graph==NULL || table_json_peek(in)!='{' || depth+1>kTableJsonMaxDepth) { in->bad=1; return 0; } + memset(map,0,sizeof(*map)); in->pos++; + for(;;) { + char c=table_json_peek(in),token[kTableJsonMaxKey]; int32_t length=0,before; int64_t number=0; int place=1; void * entry; + TableReport scratch={0}; TableJsonIn scan=*in; scan.report=&scratch; + if(c=='}') { in->pos++; return 1; } if(c==0) { in->bad=1; return 0; } + if(!table_json_scan_string(&scan,token,kTableJsonMaxKey-1,&length)) { in->pos=scan.pos; in->bad=1; return 0; } + in->pos=scan.pos; token[length]=0; + if(table_json_peek(in)!=':') { in->bad=1; return 0; } in->pos++; + if(key->kind==12) { + if(scratch.clamped || length>key->array_bound) { in->report->clamped++; place=0; } + } else { + int fits=0; + if(scratch.clamped) { in->report->kind_mismatch++; place=0; } + else if(!table_json_map_key_value(token,length,key,&number,&fits)) { in->bad=1; return 0; } + else if(!fits) { in->report->kind_mismatch++; place=0; } + } + before=map->count; entry=place ? f->place(graph->worker,map,token,length,number) : NULL; + if(place && entry==NULL) { in->bad=1; return 0; } + if(entry!=NULL && map->count==before) { in->report->duplicate++; } + c=table_json_value_shape(in); + if(entry==NULL) { if(!table_json_skip_value(in,depth+1)) { return 0; } } + else if(c!=shape && !(value->kind==17 && !value->is_array && c=='z')) { + in->report->kind_mismatch++; if(!table_json_skip_value(in,depth+1)) { return 0; } + } else if(!table_json_read_field(in,entry,value,depth+1)) { return 0; } + c=table_json_peek(in); if(c==',') { in->pos++; continue; } if(c=='}') { in->pos++; return 1; } + in->bad=1; return 0; + } +} +#endif static const TableFieldInfo schema_graphdemo_stamp_fields_[] = { - { "tag", "tag", "string", 0xbc64, 12, 0, 0, 1, 0, 8, (uint32_t) offsetof( Stamp, tag ), (uint32_t) sizeof( ( (Stamp *) 0 )->tag ), (uint32_t) offsetof( Stamp, tag_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "seq", "seq", "int32", 0xc29b, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Stamp, seq ), (uint32_t) sizeof( ( (Stamp *) 0 )->seq ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "tag", "tag", "string", 0x56d7ab194448a4f3ull, 12, 0, 0, 1, 0, 8, (uint32_t) offsetof( Stamp, tag ), (uint32_t) sizeof( ( (Stamp *) 0 )->tag ), (uint32_t) offsetof( Stamp, tag_length ), 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "seq", "seq", "int32", 0x823b8a195ce2133cull, 4, 0, 0, 0, 0, 0, (uint32_t) offsetof( Stamp, seq ), (uint32_t) sizeof( ( (Stamp *) 0 )->seq ), 0xffffffffu, 0xffffffffu, NULL, 1, 0.0, 1000.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_stamp_info_ = { "Stamp", (uint32_t) sizeof( Stamp ), 2, schema_graphdemo_stamp_fields_, schema_graphdemo_stamp_reset_raw_, 0, TableDocNone, 0, NULL }; static const TableFieldInfo schema_graphdemo_colour_fields_[] = { - { "r", "r", "uint8", 0xb019, 6, 0, 0, 0, 0, 0, (uint32_t) offsetof( Colour, r ), (uint32_t) sizeof( ( (Colour *) 0 )->r ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "g", "g", "uint8", 0xc40a, 6, 0, 0, 0, 0, 0, (uint32_t) offsetof( Colour, g ), (uint32_t) sizeof( ( (Colour *) 0 )->g ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, - { "b", "b", "uint8", 0xcae9, 6, 0, 0, 0, 0, 0, (uint32_t) offsetof( Colour, b ), (uint32_t) sizeof( ( (Colour *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL }, + { "r", "r", "uint8", 0xaf63ef4c86020cd5ull, 6, 0, 0, 0, 0, 0, (uint32_t) offsetof( Colour, r ), (uint32_t) sizeof( ( (Colour *) 0 )->r ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "g", "g", "uint8", 0xaf63da4c8601e926ull, 6, 0, 0, 0, 0, 0, (uint32_t) offsetof( Colour, g ), (uint32_t) sizeof( ( (Colour *) 0 )->g ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, + { "b", "b", "uint8", 0xaf63df4c8601f1a5ull, 6, 0, 0, 0, 0, 0, (uint32_t) offsetof( Colour, b ), (uint32_t) sizeof( ( (Colour *) 0 )->b ), 0xffffffffu, 0xffffffffu, NULL, 0, 0.0, 0.0, NULL, 0, -1, NULL, 0, NULL, NULL, -1, NULL, "", TableDocNone, 0, NULL, 0, NULL }, }; const TableTypeInfo schema_graphdemo_colour_info_ = { "Colour", (uint32_t) sizeof( Colour ), 3, schema_graphdemo_colour_fields_, schema_graphdemo_colour_reset_raw_, 0, TableDocNone, 0, NULL }; diff --git a/testdata/golden/tables/pointers-c/PartsTable.h b/testdata/golden/tables/pointers-c/PartsTable.h index f3d9f40bb..30b9d240a 100644 --- a/testdata/golden/tables/pointers-c/PartsTable.h +++ b/testdata/golden/tables/pointers-c/PartsTable.h @@ -3,9 +3,9 @@ your choice. See the LICENSE exception in the schema compiler; the compiler is AGPL-3.0, its output is not. package graphdemo — protocol id 0x36ff4b4cd88bf37a (packets only: tables version by field id, not by protocol id) - The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md): no serialize - dependency — includable from any TU. Compile the .c beside this header - to use the reflection descriptors or the text form. */ + The TABLE wire (evolution-tolerant, docs/SPEC-TABLES.md). Compile the .c + beside this header to use descriptors or JSON. 128-bit storage uses + the lane types from serialize.h. */ #ifndef SCHEMA_GRAPHDEMO_PARTSTABLE_H #define SCHEMA_GRAPHDEMO_PARTSTABLE_H @@ -13,8 +13,8 @@ #include #include /* offsetof, for the reflection descriptors */ #include /* memcpy, memset — the prefill and the wire's raw moves */ -#include /* the arena's segments (the AUTHORING path may allocate) */ #include /* the keyed accessor's None refusal, in a debug build */ +#include /* and its abort, which NDEBUG does not remove */ #include "Parts.h" @@ -109,8 +109,14 @@ typedef struct TableReport id twice is legal input whose last occurrence wins, silently (§3). */ int32_t duplicate; int malformed; /* framing damage; decode stopped, partial result kept */ + int32_t widened; /* exact widening of a known kind */ + int32_t retained, retain_lost; /* opt-in unknown-field round trips */ + int refused; /* unsupported file form, not framing damage */ + int reason; /* SCHEMA_TABLE_REFUSAL_REASON */ } TableReport; +enum SCHEMA_TABLE_REFUSAL_REASON { SCHEMA_TABLE_NO_REFUSAL, SCHEMA_TABLE_NEWER_FORM, SCHEMA_TABLE_MESSAGE_FORM_AS_FILE }; + /* ---- reflection (tables only, docs/SPEC-TABLES.md) ---- Static field descriptors for every type in the table closure: name, wire @@ -119,6 +125,7 @@ typedef struct TableReport no schema files. _table_type() returns 's descriptor. */ struct TableTypeInfo; +struct TableFieldInfo; /* One arm of a union field: where its payload sits inside the union's storage and what its payload looks like. The arm's NAME and its table-wire id ride @@ -128,6 +135,8 @@ typedef struct TableUnionArmInfo { uint32_t offset; /* offsetof the arm's payload within the union storage */ const struct TableTypeInfo * table; /* the arm payload's descriptor */ + const struct TableFieldInfo * field; /* non-body arm, relative to union storage */ + uint32_t size; /* bytes established when this arm is selected */ } TableUnionArmInfo; /* A union field's shape: the tag, and the arms indexed by it. Arms run @@ -154,7 +163,7 @@ typedef struct TableUnionInfo typedef struct TableVariantInfo { const char * name; - uint16_t id; + uint64_t id; } TableVariantInfo; /* THE SHARED EMPTY DOC (docs/SPEC-TABLES.md §8.1): a declaration with no /// @@ -164,12 +173,14 @@ typedef struct TableVariantInfo are one, so every absent doc compares equal by address. */ static SCHEMA_UNUSED const char TableDocNone[1] = ""; +typedef struct TableWideRange { uint64_t lo[2], hi[2]; } TableWideRange; + typedef struct TableFieldInfo { const char * name; /* schema field name, e.g. "health" */ const char * json; /* the TEXT form's key: the json = "key" attribute, else name (§16.3) */ const char * type_name; /* schema type name, e.g. "float32", "Grade" */ - uint16_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ + uint64_t id; /* table-wire field id (name hash; the was alias's hash after a rename) */ uint8_t kind; /* table-wire kind; for arrays/strings/bytes, the ELEMENT kind */ int is_array; /* fixed or counted array (bytes included) */ int is_pointer; /* a *T pointer field: storage is an 8-byte TableRef; the target is a table */ @@ -184,6 +195,8 @@ typedef struct TableFieldInfo int has_range; /* a declared [min, max] (int or float) */ double range_min; /* NOTE: int64 ranges beyond 2^53 lose precision here */ double range_max; + const TableWideRange * wide; /* exact raw range for wide/fixed storage */ + int32_t frac_bits; int64_t enum_max; /* enums: highest valid value (None = 0 always valid); unions: the arm count (tag range [0, enum_max]); flags: the highest declared BIT INDEX; else -1 */ @@ -211,6 +224,8 @@ typedef struct TableFieldInfo const char * doc; int32_t num_tags; const char * const * tags; + int sequence; /* 0 inline field, 1 unbounded list, 2 map */ + void * (*place)(void * worker,void * slot,const char * key,int32_t length,int64_t number); } TableFieldInfo; typedef struct TableTypeInfo @@ -237,157 +252,277 @@ typedef struct TableTypeInfo const char * const * tags; } TableTypeInfo; + +/* Each save owns one bounded identity table. Probes share it and rewind their + additions, so nesting copies only the writer cursor. Reverse-order removal + preserves every earlier open-addressing chain and first-use wire reference. */ +typedef struct TableWriteIds +{ + uint64_t ids[48]; + uint32_t slots[128], positions[48]; + int count; +} TableWriteIds; + typedef struct TableWriter { uint8_t * buffer; - int64_t capacity; - int64_t offset; - int overflow; + int64_t capacity, offset; + int overflow, id_checkpoint, check_default; + TableWriteIds * vocabulary; + struct TableNumbering * nodes; } TableWriter; -static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity ) +static SCHEMA_UNUSED TableWriter table_writer_make( uint8_t * buffer, int64_t capacity, TableWriteIds * vocabulary ) { TableWriter w; - w.buffer = buffer; - w.capacity = capacity; - w.offset = 0; - w.overflow = 0; + memset( &w, 0, sizeof( w ) ); + w.buffer = buffer; w.capacity = capacity; w.vocabulary=vocabulary; + memset(vocabulary,0,sizeof(*vocabulary)); return w; } - static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_raw( TableWriter * w, const void * data, int64_t bytes ) { - if ( w->offset + bytes > w->capacity ) { w->overflow = 1; return; } - memcpy( w->buffer + w->offset, data, (size_t) bytes ); + if ( bytes < 0 || w->offset > w->capacity || bytes > w->capacity - w->offset ) { w->overflow = 1; return; } + if ( w->buffer != NULL && bytes != 0 ) { memcpy( w->buffer + w->offset, data, (size_t) bytes ); } w->offset += bytes; } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put8( TableWriter * w, uint8_t v ) { table_writer_raw( w, &v, 1 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put16( TableWriter * w, uint16_t v ) { - uint8_t b[2]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); - table_writer_raw( w, b, 2 ); + uint8_t b[2]; b[0] = (uint8_t) v; b[1] = (uint8_t) (v >> 8); table_writer_raw( w, b, 2 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put32( TableWriter * w, uint32_t v ) { - uint8_t b[4]; - b[0] = (uint8_t) v; b[1] = (uint8_t) ( v >> 8 ); b[2] = (uint8_t) ( v >> 16 ); b[3] = (uint8_t) ( v >> 24 ); - table_writer_raw( w, b, 4 ); + uint8_t b[4]; int i; for ( i = 0; i < 4; i++ ) { b[i] = (uint8_t) (v >> (8*i)); } table_writer_raw( w, b, 4 ); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_put64( TableWriter * w, uint64_t v ) { - table_writer_put32( w, (uint32_t) v ); - table_writer_put32( w, (uint32_t) ( v >> 32 ) ); + table_writer_put32( w, (uint32_t) v ); table_writer_put32( w, (uint32_t) (v >> 32) ); +} +static SCHEMA_UNUSED void table_writer_leb( TableWriter * w, uint64_t v ) +{ + do { uint8_t b = (uint8_t) (v & 127); v >>= 7; table_writer_put8( w, (uint8_t) (b | (v ? 128 : 0)) ); } while ( v ); +} +static SCHEMA_UNUSED void table_writer_id( TableWriter * w, uint64_t id ) +{ + TableWriteIds * v=w->vocabulary; + uint64_t hash=id; + uint32_t slot,index; + hash ^= hash>>33; hash *= UINT64_C(0xff51afd7ed558ccd); hash ^= hash>>33; + slot=(uint32_t)hash & (128-1); + while((index=v->slots[slot])!=0) { + if(v->ids[index-1]==id) { table_writer_leb(w,index); return; } + slot=(slot+1)&(128-1); + } + if(v->count==48) { w->overflow=1; return; } + v->ids[v->count]=id; v->positions[v->count]=slot; v->count++; + v->slots[slot]=(uint32_t)v->count; table_writer_leb(w,(uint64_t)v->count); +} +static SCHEMA_UNUSED TableWriter table_writer_probe( const TableWriter * w ) +{ + TableWriter probe = *w; + probe.buffer = NULL; probe.capacity = INT64_MAX; probe.offset = 0; probe.overflow = 0; + probe.id_checkpoint=w->vocabulary->count; + return probe; +} +static SCHEMA_UNUSED void table_writer_rewind( const TableWriter * probe ) +{ + TableWriteIds * v=probe->vocabulary; + while(v->count>probe->id_checkpoint) { v->count--; v->slots[v->positions[v->count]]=0; } } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE void table_writer_patch32( TableWriter * w, int64_t at, uint32_t v ) +static SCHEMA_UNUSED void table_writer_finish( TableWriter * w ) { - if ( at + 4 > w->capacity ) { w->overflow = 1; return; } - w->buffer[at] = (uint8_t) v; w->buffer[at+1] = (uint8_t) ( v >> 8 ); - w->buffer[at+2] = (uint8_t) ( v >> 16 ); w->buffer[at+3] = (uint8_t) ( v >> 24 ); + int i; for ( i = 0; i < w->vocabulary->count; i++ ) { table_writer_put64( w, w->vocabulary->ids[i] ); } + table_writer_put64( w, (uint64_t) w->vocabulary->count ); } typedef struct TableReader { const uint8_t * buffer; - int64_t size; - int64_t offset; + int64_t size, offset; TableReport * report; + const uint8_t * ids; + struct TableNodeMap * nodes; + uint64_t id_count; + int nested; } TableReader; static SCHEMA_UNUSED TableReader table_reader_make( const uint8_t * buffer, int64_t size, TableReport * report ) { TableReader r; - r.buffer = buffer; - r.size = size; - r.offset = 0; - r.report = report; + memset( &r, 0, sizeof( r ) ); r.buffer = buffer; r.size = size; r.report = report; r.nested = 1; return r; } - -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t bytes ) { return bytes >= 0 && r->offset >= 0 && r->offset <= r->size && bytes <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_has( const TableReader * r, int64_t n ) +{ return n >= 0 && r->offset >= 0 && r->offset <= r->size && n <= r->size - r->offset; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int table_reader_room( const TableReader * r, uint64_t n ) +{ return r->offset >= 0 && r->offset <= r->size && n <= (uint64_t) (r->size - r->offset); } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint8_t table_reader_get8( TableReader * r ) { return r->buffer[r->offset++]; } static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint16_t table_reader_get16( TableReader * r ) +{ uint16_t v = r->buffer[r->offset]; v |= (uint16_t) ((uint16_t) r->buffer[r->offset+1] << 8); r->offset += 2; return v; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +{ uint32_t v = 0; int i; for ( i = 0; i < 4; i++ ) { v |= (uint32_t) table_reader_get8( r ) << (8*i); } return v; } +static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +{ uint64_t lo = table_reader_get32( r ); return lo | ((uint64_t) table_reader_get32( r ) << 32); } +static SCHEMA_UNUSED uint64_t table_reader_id_at( const TableReader * r, uint64_t ref ) { - uint16_t v = (uint16_t) ( (uint16_t) r->buffer[r->offset] | ( (uint16_t) r->buffer[r->offset+1] << 8 ) ); - r->offset += 2; - return v; + TableReader entry = table_reader_make( r->ids + (ref-1)*8, 8, r->report ); + return table_reader_get64( &entry ); } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint32_t table_reader_get32( TableReader * r ) +/* Rejected numbers do not consume input: the containing body's exact extent + must still be able to detect the damaged spelling. */ +static SCHEMA_UNUSED int table_reader_leb( TableReader * r, uint64_t * value ) { - uint32_t v = (uint32_t) r->buffer[r->offset] | ( (uint32_t) r->buffer[r->offset+1] << 8 ) - | ( (uint32_t) r->buffer[r->offset+2] << 16 ) | ( (uint32_t) r->buffer[r->offset+3] << 24 ); - r->offset += 4; - return v; + int i; int64_t at = r->offset; *value = 0; + for ( i = 0; i < 10; i++ ) + { + uint8_t b; + if ( !table_reader_has( r, 1 ) ) { r->offset = at; return 0; } + b = table_reader_get8( r ); + if ( i == 9 && b > 1 ) { r->offset = at; return 0; } + *value |= (uint64_t) (b & 127) << (7*i); + if ( (b & 128) == 0 ) { if ( i && b == 0 ) { r->offset = at; return 0; } return 1; } + } + r->offset = at; return 0; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE uint64_t table_reader_get64( TableReader * r ) +static SCHEMA_UNUSED TableReader table_reader_child( const TableReader * r, int64_t offset, int64_t bytes ) { - uint64_t lo = table_reader_get32( r ); - uint64_t hi = table_reader_get32( r ); - return lo | ( hi << 32 ); + TableReader child = *r; + child.buffer = r->buffer + offset; child.size = bytes; child.offset = 0; child.nested = 1; + return child; +} +static SCHEMA_UNUSED int table_reader_span( TableReader * r, TableReader * sub ) +{ + uint64_t bytes; + if ( !table_reader_leb( r, &bytes ) || !table_reader_room( r, bytes ) ) { return 0; } + *sub = table_reader_child( r, r->offset, (int64_t) bytes ); r->offset += (int64_t) bytes; return 1; +} +/* The reference reads the array header in the enclosing body, then bounds + elements by L. Keep that recovery order even when the count crosses L. */ +static SCHEMA_UNUSED int table_reader_array_header( TableReader * sub, const TableReader * parent, uint8_t * kind, uint64_t * count ) +{ + TableReader header = *sub; + int ok; + header.size += parent->size - parent->offset; + *kind = table_reader_get8( &header ); + ok = table_reader_leb( &header, count ); sub->offset = header.offset; + return ok; } - -/* skip one payload by kind; 0 = framing damage */ static SCHEMA_UNUSED int table_reader_skip( TableReader * r, uint8_t kind ) { + int width = 0; uint64_t n; switch ( kind ) { - case 1: case 2: case 6: + case 1: case 2: case 6: case 20: case 25: width = 1; break; + case 3: case 7: case 21: case 26: width = 2; break; + case 4: case 8: case 10: case 22: case 27: width = 4; break; + case 5: case 9: case 11: case 23: case 28: width = 8; break; + case 18: case 19: case 24: case 29: width = 16; break; + case 17: case 30: return table_reader_leb( r, &n ); + case 15: + if ( !table_reader_leb( r, &n ) ) { return 0; } + if ( n == 0 ) { return 1; } if ( !table_reader_has( r, 1 ) ) { return 0; } - r->offset += 1; return 1; - case 3: case 7: - if ( !table_reader_has( r, 2 ) ) { return 0; } - r->offset += 2; return 1; - /* 17 is a NODE INDEX (docs/SPEC-TABLES.md 3.1): four bytes, so it costs - one row here and a reader without the kind still skips a pointer field */ - case 4: case 8: case 10: case 17: - if ( !table_reader_has( r, 4 ) ) { return 0; } - r->offset += 4; return 1; - case 5: case 9: case 11: - if ( !table_reader_has( r, 8 ) ) { return 0; } - r->offset += 8; return 1; - case 12: case 13: case 14: case 16: - { - uint32_t n; - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - case 15: /* union: u16 arm id, then the arm length-prefixed (id 0 = empty, no body) */ - { - uint32_t n; - if ( !table_reader_has( r, 2 ) ) { return 0; } - if ( table_reader_get16( r ) == 0 ) { return 1; } - if ( !table_reader_has( r, 4 ) ) { return 0; } - n = table_reader_get32( r ); - if ( !table_reader_has( r, n ) ) { return 0; } - r->offset += n; - return 1; - } - default: break; + r->offset++; + /* fall through */ + case 12: case 13: case 14: case 16: case 31: case 32: case 33: + if ( !table_reader_leb( r, &n ) || !table_reader_room( r, n ) ) { return 0; } + r->offset += (int64_t) n; return 1; + default: return 0; } - return 0; + if ( !table_reader_has( r, width ) ) { return 0; } r->offset += width; return 1; +} +static SCHEMA_UNUSED int table_kind_widens( uint8_t kind, uint8_t declared ) +{ + if ( declared >= 3 && declared <= 5 ) { return kind >= 2 && kind < declared; } + if ( declared >= 7 && declared <= 9 ) { return kind >= 6 && kind < declared; } + if ( declared == 18 ) { return kind >= 2 && kind <= 5; } + if ( declared == 19 ) { return kind >= 6 && kind <= 9; } + return declared == 11 && kind == 10; +} +static SCHEMA_UNUSED double table_wire_widen_f32( uint32_t bits ) +{ + if ( (bits & 0x7f800000u) == 0x7f800000u && (bits & 0x007fffffu) != 0 ) + { + uint64_t wide = ((uint64_t) (bits >> 31) << 63) | 0x7ff0000000000000ull | ((uint64_t) (bits & 0x007fffffu) << 29); + double d; memcpy( &d, &wide, 8 ); return d; + } + { float f; memcpy( &f, &bits, 4 ); return (double) f; } +} +static SCHEMA_UNUSED int table_wire_open( TableReader * r, const uint8_t * buffer, int64_t bytes, TableReport * report ) +{ + uint64_t count, i, j; int64_t span; TableReader tail; + if ( bytes < 1 || buffer == NULL ) { report->malformed = 1; return 0; } + if ( buffer[0] != 1 ) { report->refused = 1; report->reason = buffer[0] == 2 ? SCHEMA_TABLE_MESSAGE_FORM_AS_FILE : SCHEMA_TABLE_NEWER_FORM; return 0; } + if ( bytes < 9 ) { report->malformed = 1; return 0; } + tail = table_reader_make( buffer + bytes - 8, 8, report ); count = table_reader_get64( &tail ); + if ( count > (uint64_t) ((bytes - 9) / 8) ) { report->malformed = 1; return 0; } + span = (int64_t) count * 8 + 8; + *r = table_reader_make( buffer + 1, bytes - span - 1, report ); + r->ids = buffer + bytes - span; r->id_count = count; r->nested = 0; + for ( i = 1; i < count; i++ ) { for ( j = 0; j < i; j++ ) { + if ( table_reader_id_at( r, i+1 ) == table_reader_id_at( r, j+1 ) ) { report->malformed = 1; return 0; } + } } + /* Root slack invalidates the whole file before any overlay. A stopped + body is different: its successfully decoded prefix must survive. */ + tail = *r; + for ( ;; ) + { + uint64_t ref; uint8_t kind; + if ( !table_reader_leb( &tail, &ref ) ) { break; } + if ( ref == 0 ) { if ( tail.offset != tail.size ) { report->malformed = 1; return 0; } break; } + if ( ref > count || !table_reader_has( &tail, 1 ) ) { break; } + kind = table_reader_get8( &tail ); if ( !table_reader_skip( &tail, kind ) ) { break; } + } + return 1; +} +/* UTF-8 admits Unicode scalar values except U+0000, which cannot be held in + the generated terminated string storage. */ +static SCHEMA_UNUSED int table_wire_utf8( const uint8_t * p, uint64_t n ) +{ + uint64_t i = 0; + while ( i < n ) + { + uint8_t b = p[i++]; uint32_t cp; int more, j; + if ( b == 0 ) { return 0; } if ( b < 128 ) { continue; } + if ( b >= 194 && b <= 223 ) { cp = b & 31; more = 1; } + else if ( b >= 224 && b <= 239 ) { cp = b & 15; more = 2; } + else if ( b >= 240 && b <= 244 ) { cp = b & 7; more = 3; } + else { return 0; } + if ( (uint64_t) more > n-i ) { return 0; } + for ( j = 0; j < more; j++ ) { b = p[i++]; if ( (b & 192) != 128 ) { return 0; } cp = (cp << 6) | (b & 63); } + if ( (more == 2 && cp < 2048) || (more == 3 && cp < 65536) || cp > 1114111 || (cp >= 55296 && cp <= 57343) ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED uint64_t table_wire_utf8_clamp( const uint8_t * p, uint64_t n, uint64_t cap ) +{ + if ( n <= cap ) { return n; } while ( cap && (p[cap] & 192) == 128 ) { cap--; } return cap; } -/* The storage index a key names, with the None refusal that stands in EVERY - build. The storage shifts left and holds no slot for None, so a build that - skipped this compare would index one element BEFORE the array — undefined - behaviour in the configuration a game ships. */ -static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key ) +#ifndef schema_assert +#define schema_assert assert +#endif +#ifndef schema_fatal +#define schema_fatal abort +#endif + +/* The storage index a key names. Both bounds stand in EVERY build: None, + negative values and keys beyond the enum's maximum all refuse. */ +static SCHEMA_UNUSED int32_t table_keyed_slot( int32_t key, uint32_t count ) { - if ( key == 0 ) + if ( (uint32_t) key - 1u >= count ) { - assert( 0 && "None is the null key of an enum-keyed array: it keys no slot" ); - abort(); + schema_assert( 0 && "key is outside the enum-keyed array" ); + schema_fatal(); } return key - 1; } -/* keyed[key] — the slot a variant owns, as an LVALUE. The key is evaluated +/* SCHEMA_TABLE_KEYED_AT(slots,key,E_MAX) is an LVALUE. The key is evaluated once. ITERATION is the surface a consumer of the whole array wants: walk 1..E_MAX and index with the key, so a call site writes no shift. */ -#define SCHEMA_TABLE_KEYED_AT( array, key ) ( (array)[ table_keyed_slot( (int32_t) ( key ) ) ] ) +#define SCHEMA_TABLE_KEYED_AT( array, key, count ) ( (array)[ table_keyed_slot( (int32_t) ( key ), (uint32_t) ( count ) ) ] ) static SCHEMA_UNUSED float table_bits_to_float( uint32_t bits ) { float f; memcpy( &f, &bits, 4 ); return f; } @@ -397,6 +532,94 @@ static SCHEMA_UNUSED uint64_t table_double_to_bits( double d ) { uint64_t b; mem #endif /* SCHEMA_GRAPHDEMO_TABLE_PRIMITIVES */ +#ifndef SCHEMA_TABLE_UTF16_RUNTIME +#define SCHEMA_TABLE_UTF16_RUNTIME +static SCHEMA_UNUSED uint16_t table_wire_utf16_unit( const uint8_t * bytes, int64_t index ) +{ + return (uint16_t) ((uint16_t) bytes[index*2] | ((uint16_t) bytes[index*2+1] << 8)); +} +static SCHEMA_UNUSED int table_wire_utf16( const uint8_t * bytes, int64_t units ) +{ + int64_t i = 0; + while ( i < units ) { + uint16_t unit = table_wire_utf16_unit( bytes, i++ ); + if ( unit == 0 ) { return 0; } + if ( unit >= 0xd800 && unit <= 0xdbff ) { + uint16_t low; + if ( i == units ) { return 0; } + low = table_wire_utf16_unit( bytes, i++ ); + if ( low < 0xdc00 || low > 0xdfff ) { return 0; } + } else if ( unit >= 0xdc00 && unit <= 0xdfff ) { return 0; } + } + return 1; +} +static SCHEMA_UNUSED int64_t table_wire_utf16_clamp( const uint8_t * bytes, int64_t units, int64_t bound ) +{ + if ( units <= bound ) { return units; } + if ( bound > 0 ) { + uint16_t last = table_wire_utf16_unit( bytes, bound-1 ); + if ( last >= 0xd800 && last <= 0xdbff ) { bound--; } + } + return bound; +} +#endif + +#ifndef SCHEMA_TABLE_REFUSE_RUNTIME +#define SCHEMA_TABLE_REFUSE_RUNTIME +typedef int TableRefuseReason; +enum { + SCHEMA_TABLE_REFUSE_OK=0, + SCHEMA_TABLE_REFUSE_NOT_A_COOK=1, + SCHEMA_TABLE_REFUSE_FOREIGN_ORDER=2, + SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION=3, + SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO=4, + SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT=5, + SCHEMA_TABLE_REFUSE_TRUNCATED=6, + SCHEMA_TABLE_REFUSE_UNALIGNED_BASE=7, + SCHEMA_TABLE_REFUSE_BAD_LAYOUT=8, + SCHEMA_TABLE_REFUSE_UNKNOWN_FORM=9, + SCHEMA_TABLE_REFUSE_COUNT_OVER_LENGTH=10, + SCHEMA_TABLE_REFUSE_COUNT_OVER_EXTENT_CAP=11, + SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP=12, + SCHEMA_TABLE_REFUSE_DATA_CYCLE=13 +}; +static SCHEMA_UNUSED const uint8_t * table_cook_refuse(TableRefuseReason * out,TableRefuseReason reason) +{ if(out!=NULL) { *out=reason; } return NULL; } +#endif + + +#ifndef SCHEMA_TABLE_ALLOCATOR_RUNTIME +#define SCHEMA_TABLE_ALLOCATOR_RUNTIME +#ifndef schema_allocate +#include +#define schema_allocate(bytes) calloc(1,(size_t)(bytes)) +#endif +#ifndef schema_release +#include +#define schema_release(pointer) free(pointer) +#endif +typedef struct TableAllocator +{ + void * (*alloc)(void * context,int64_t bytes); + void (*free)(void * context,void * pointer); + void * context; +} TableAllocator; +static SCHEMA_UNUSED void * table_default_allocate(void * context,int64_t bytes) +{ (void)context; return bytes>0 && (uint64_t)bytes<=SIZE_MAX ? schema_allocate(bytes) : NULL; } +static SCHEMA_UNUSED void table_default_release(void * context,void * pointer) +{ (void)context; schema_release(pointer); } +static SCHEMA_UNUSED TableAllocator table_default_allocator(void) +{ + TableAllocator allocator={table_default_allocate,table_default_release,NULL}; return allocator; +} +static SCHEMA_UNUSED void * table_allocate(TableAllocator allocator,int64_t bytes) +{ + if(allocator.alloc==NULL || allocator.free==NULL || bytes<=0 || (uint64_t)bytes>SIZE_MAX) { return NULL; } + return allocator.alloc(allocator.context,bytes); +} +static SCHEMA_UNUSED void table_release(TableAllocator allocator,void * pointer) +{ if(allocator.free!=NULL) { allocator.free(allocator.context,pointer); } } +#endif #ifndef SCHEMA_GRAPHDEMO_TABLE_ARENA #define SCHEMA_GRAPHDEMO_TABLE_ARENA @@ -563,10 +786,11 @@ typedef struct TableArena { TableAtomicPtr segments[ kTableMaxSegments ]; TableAtomicU32 cursor; /* (segment << kTableSegmentBits) | bytes handed out */ + TableAllocator allocator; int locked; /* MONOTONIC: Lock is one-way, there is no unlock */ } TableArena; -static SCHEMA_UNUSED void table_arena_init( TableArena * arena ) +static SCHEMA_UNUSED void table_arena_init_with_allocator( TableArena * arena, TableAllocator allocator ) { uint32_t i; for ( i = 0; i < kTableMaxSegments; i++ ) @@ -574,9 +798,12 @@ static SCHEMA_UNUSED void table_arena_init( TableArena * arena ) table_atomic_store_ptr( &arena->segments[i], (uint8_t *) NULL ); } table_atomic_store32( &arena->cursor, 0 ); - arena->locked = 0; + arena->locked = 0; arena->allocator=allocator; } +static SCHEMA_UNUSED void table_arena_init(TableArena * arena) +{ table_arena_init_with_allocator(arena,table_default_allocator()); } + static SCHEMA_UNUSED void table_arena_shutdown( TableArena * arena ) { uint32_t i; @@ -586,7 +813,7 @@ static SCHEMA_UNUSED void table_arena_shutdown( TableArena * arena ) if ( segment != NULL ) { uint8_t * expected = segment; - if ( table_arena_cas_ptr( &arena->segments[i], &expected, (uint8_t *) NULL ) ) { free( segment ); } + if ( table_arena_cas_ptr( &arena->segments[i], &expected, (uint8_t *) NULL ) ) { table_release(arena->allocator,segment); } } } table_atomic_store32( &arena->cursor, 0 ); @@ -621,12 +848,12 @@ static SCHEMA_UNUSED uint32_t table_arena_grab_slab( TableArena * arena ) region. It costs nothing measurable: a fresh segment is untouched pages either way, and calloc has the kernel hand them over zeroed. */ - uint8_t * memory = (uint8_t *) calloc( 1, kTableSegmentSize ); + uint8_t * memory = (uint8_t *) table_allocate(arena->allocator,kTableSegmentSize); uint8_t * expected = NULL; if ( memory == NULL ) { return kTableAllocFailed; } if ( !table_arena_cas_ptr( &arena->segments[segment], &expected, memory ) ) { - free( memory ); /* another worker published this segment first */ + table_release(arena->allocator,memory); /* another worker published this segment first */ } } if ( table_arena_cas32( &arena->cursor, &cursor, cursor + kTableSlabBytes ) ) @@ -672,24 +899,44 @@ static SCHEMA_UNUSED TableWorker table_worker_make( TableArena * arena ) arena offset, or kTableAllocFailed. It is the untyped half of every generated _emplace: the type's own size and its reset stay in the generated code, where they can be spelled. */ -static SCHEMA_UNUSED uint32_t table_worker_bump( TableWorker * worker, uint32_t bytes ) +static SCHEMA_UNUSED uint32_t table_arena_grab_span( TableArena * arena, int64_t bytes ) +{ + uint32_t spanned; + if(bytes<=0 || bytes>((int64_t)kTableMaxSegments-2)*kTableSegmentSize) { return kTableAllocFailed; } + spanned=(uint32_t)((bytes+kTableSegmentSize-1)>>kTableSegmentBits); + for(;;) + { + uint32_t cursor=table_atomic_load32(&arena->cursor); + uint32_t start=(cursor>>kTableSegmentBits)+1; + uint32_t next; + uint8_t * memory; + if(start+spanned>=kTableMaxSegments) { return kTableAllocFailed; } + next=(start+spanned)<cursor,&cursor,next)) { continue; } + memory=(uint8_t *)table_allocate(arena->allocator,bytes); + if(memory==NULL) { return kTableAllocFailed; } + table_atomic_store_ptr(&arena->segments[start],memory); + return start<arena == NULL || worker->arena->locked ) { return kTableAllocFailed; } - bytes = table_align_up( bytes ); - if ( bytes > kTableSlabBytes ) { return kTableAllocFailed; } /* a node larger than a slab: refused, never split */ - if ( worker->end == 0 || worker->next + bytes > worker->end ) + if(worker->arena==NULL || worker->arena->locked || bytes<=0 || bytes>INT64_MAX-kTableAlign) { return kTableAllocFailed; } + bytes=table_align_up64(bytes); + if(bytes>kTableSlabBytes) { return table_arena_grab_span(worker->arena,bytes); } + if(worker->end==0 || (uint64_t)worker->next+(uint64_t)bytes>worker->end || (worker->next==0 && bytes+kTableAlign>kTableSlabBytes)) { - uint32_t offset = table_arena_grab_slab( worker->arena ); - if ( offset == kTableAllocFailed ) { return kTableAllocFailed; } - worker->next = offset; - worker->end = offset + kTableSlabBytes; - if ( worker->next == 0 ) { worker->next = kTableAlign; } /* offset 0 is null: the arena's head stays reserved */ + uint32_t offset=table_arena_grab_slab(worker->arena); + if(offset==kTableAllocFailed) { return kTableAllocFailed; } + worker->next=offset; worker->end=offset+kTableSlabBytes; + if(worker->next==0) { worker->next=kTableAlign; } + if((uint64_t)worker->next+(uint64_t)bytes>worker->end) { return table_arena_grab_span(worker->arena,bytes); } } - at = worker->next; - worker->next += bytes; - return at; + at=worker->next; worker->next+=(uint32_t)bytes; return at; } +static SCHEMA_UNUSED uint32_t table_worker_bump( TableWorker * worker, uint32_t bytes ) +{ return table_worker_alloc(worker,bytes); } /* ---- the resolution context: which encoding a walk is reading ---- @@ -727,6 +974,469 @@ typedef struct TableSink #endif /* SCHEMA_GRAPHDEMO_TABLE_ARENA */ +#ifndef SCHEMA_TABLE_BLOB_RUNTIME +#define SCHEMA_TABLE_BLOB_RUNTIME +typedef struct TableBlob { uint32_t length, zero; } TableBlob; +typedef struct TableBytesView { const uint8_t * data; int64_t length; } TableBytesView; +typedef struct TableStringView { const char * data; int64_t length; } TableStringView; +#define kTableBytesTypeId UINT64_C(0x2f2ec0474f1c4fe4) +#define kTableStringTypeId UINT64_C(0x704be0d8faaffc58) + +static SCHEMA_UNUSED int64_t table_blob_storage( int64_t length, int terminated ) +{ + if ( length < 0 || (uint64_t)length > UINT32_MAX ) { return -1; } + return table_align_up64(8+length+(terminated ? 1 : 0)); +} + +static SCHEMA_UNUSED const TableBlob * table_blob_at( const TableCtx * ctx, const TableRef * ref ) +{ + if ( ref->value == 0 ) { return NULL; } + return (const TableBlob *)(const void *)(ctx != NULL && ctx->arena != NULL + ? table_arena_at(ctx->arena,(uint32_t)ref->value) : (const uint8_t *)(const void *)ref+ref->value); +} +static SCHEMA_UNUSED TableBytesView table_bytes_at( const TableCtx * ctx, const TableRef * ref ) +{ + const TableBlob * blob=table_blob_at(ctx,ref); + TableBytesView view={NULL,0}; + if(blob!=NULL) { view.data=(const uint8_t *)(blob+1); view.length=blob->length; } return view; +} +static SCHEMA_UNUSED TableStringView table_string_at( const TableCtx * ctx, const TableRef * ref ) +{ + const TableBlob * blob=table_blob_at(ctx,ref); + TableStringView view={NULL,0}; + if(blob!=NULL) { view.data=(const char *)(blob+1); view.length=blob->length; } return view; +} +static SCHEMA_UNUSED TableBlob * table_blob_emplace( TableWorker * worker, TableRef * ref, int64_t length, int terminated ) +{ + int64_t bytes=table_blob_storage(length,terminated); + uint32_t at; + TableBlob * blob; + if(bytes<0) { return NULL; } + at=table_worker_alloc(worker,bytes); + if(at==kTableAllocFailed) { return NULL; } + blob=(TableBlob *)(void *)table_arena_at(worker->arena,at); + blob->length=(uint32_t)length; blob->zero=0; ref->value=at; return blob; +} +static SCHEMA_UNUSED uint8_t * table_bytes_emplace( TableWorker * worker, TableRef * ref, int64_t length ) +{ + TableBlob * blob=table_blob_emplace(worker,ref,length,0); + return blob!=NULL ? (uint8_t *)(blob+1) : NULL; +} +static SCHEMA_UNUSED char * table_string_emplace( TableWorker * worker, TableRef * ref, const char * text, int64_t length ) +{ + TableBlob * blob=table_blob_emplace(worker,ref,length,1); + char * data; + if(blob==NULL) { return NULL; } + data=(char *)(blob+1); if(text!=NULL && length>0) { memcpy(data,text,(size_t)length); } data[length]=0; return data; +} +#endif + +#ifndef SCHEMA_TABLE_GRAPH_RUNTIME +#define SCHEMA_TABLE_GRAPH_RUNTIME +typedef struct TableNumbering TableNumbering; +typedef struct TableNodeType +{ + uint64_t id; + int64_t size; + int (*save)( TableWriter * w, const void * value ); + int (*edges)( TableNumbering * numbering, const void * value ); + int blob; /* 0 table, 1 bytes, 2 terminated UTF-8 */ + int (*extent)(TableNumbering *,const void *,uint8_t *,int64_t *); + int64_t (*wire_storage)(const TableReader *); + int64_t alignment; + int has_extent; + int (*cook_body)(struct TableNumbering *,uint8_t *,const void *,int); + int (*cook_extent)(TableNumbering *,const void *,uint8_t *,uint8_t *,int64_t *,int); +} TableNodeType; + +typedef struct TableNodeEntry +{ + const void * value; + const TableNodeType * type; + uint64_t packed_offset; + int open; +} TableNodeEntry; + +typedef struct TableNodeWork +{ + const void * value; + const TableNodeType * type; + uint64_t index; + const TableRef * slot; + int close; +} TableNodeWork; + +struct TableNumbering +{ + const TableCtx * ctx; + uint8_t * pack_base; +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + TableSequenceOrder * orders; +#endif + TableAllocator allocator; + TableNodeEntry * entries; + uint64_t count, capacity; + uint64_t * slots; + uint64_t slot_capacity; + TableNodeWork * work; + uint64_t work_count, work_capacity; +}; + +static SCHEMA_UNUSED int64_t table_node_storage( TableNumbering * n, const TableNodeType * type, const void * value ) +{ + int64_t at=type->size; + if(type->blob) { return table_blob_storage(((const TableBlob *)value)->length,type->blob==2); } + if(type->has_extent) { + int64_t extent=0; + if(!type->cook_extent(n,value,NULL,NULL,&extent,1) || at>INT64_MAX-kTableAlign) { return -1; } + at=table_align_up64(at); if(extent>INT64_MAX-at) { return -1; } at+=extent; + } + if(at>INT64_MAX-kTableAlign) { return -1; } return table_align_up64(at); +} +static SCHEMA_UNUSED int64_t table_node_wire_storage( const TableNodeType * type, const TableReader * body ) +{ + if(type->blob && body->size>UINT32_MAX) { + body->report->reason=SCHEMA_TABLE_REFUSE_BLOB_OVER_SIZE_CAP; + return -1; + } + return type->blob ? table_blob_storage(body->size,type->blob==2) : type->wire_storage(body); +} + +static SCHEMA_UNUSED int table_blob_save( TableWriter * w, const void * value ) +{ + const TableBlob * blob=(const TableBlob *)value; + table_writer_raw(w,blob+1,blob->length); return !w->overflow; +} +static SCHEMA_UNUSED int table_blob_edges( TableNumbering * n, const void * value ) +{ (void)n; (void)value; return 1; } +static const TableNodeType table_bytes_node_type = { kTableBytesTypeId, 0, table_blob_save, table_blob_edges, 1, NULL, NULL, 8, 0, NULL, NULL }; +static const TableNodeType table_string_node_type = { kTableStringTypeId, 0, table_blob_save, table_blob_edges, 2, NULL, NULL, 8, 0, NULL, NULL }; + + +static SCHEMA_UNUSED uint64_t table_number_hash( const void * value ) +{ + uint64_t x = (uint64_t)(uintptr_t)value; + x ^= x >> 33; x *= UINT64_C(0xff51afd7ed558ccd); + x ^= x >> 33; x *= UINT64_C(0xc4ceb9fe1a85ec53); + return x ^ (x >> 33); +} + +static SCHEMA_UNUSED void * table_number_grow( TableAllocator allocator, void * memory, uint64_t * capacity, size_t width ) +{ + uint64_t next = *capacity ? *capacity * 2 : 64; + void * grown; + if ( next < *capacity || next > INT64_MAX / width ) { return 0; } + grown = table_allocate(allocator,(int64_t)next*(int64_t)width); + if ( grown == NULL ) { return 0; } + if(memory!=NULL) { memcpy(grown,memory,(size_t)*capacity*width); } + table_release(allocator,memory); *capacity = next; return grown; +} + +static SCHEMA_UNUSED int table_number_rehash( TableNumbering * n ) +{ + uint64_t capacity = n->slot_capacity ? n->slot_capacity * 2 : 128; + uint64_t * slots; + uint64_t i; + if ( capacity < n->slot_capacity || capacity > INT64_MAX / sizeof(uint64_t) ) { return 0; } + slots = (uint64_t *)table_allocate(n->allocator,(int64_t)capacity*sizeof(uint64_t)); + if ( slots == NULL ) { return 0; } + for ( i = 0; i < n->count; i++ ) + { + uint64_t at = table_number_hash(n->entries[i].value) & (capacity-1); + while ( slots[at] ) { at = (at+1) & (capacity-1); } + slots[at] = i+1; + } + table_release(n->allocator,n->slots); n->slots = slots; n->slot_capacity = capacity; return 1; +} + +static SCHEMA_UNUSED uint64_t table_number_find( const TableNumbering * n, const void * value ) +{ + uint64_t at; + if ( value == NULL || n == NULL || n->slot_capacity == 0 ) { return 0; } + at = table_number_hash(value) & (n->slot_capacity-1); + while ( n->slots[at] ) + { + uint64_t index = n->slots[at]; + if ( n->entries[index-1].value == value ) { return index; } + at = (at+1) & (n->slot_capacity-1); + } + return 0; +} + +static SCHEMA_UNUSED int table_number_edge( TableNumbering * n, const void * value, const TableNodeType * type ) +{ + TableNodeWork entry; + if ( value == NULL ) { return 1; } + if ( n->work_count == n->work_capacity ) + { + void * grown = table_number_grow(n->allocator,n->work,&n->work_capacity,sizeof(TableNodeWork)); + if ( grown == NULL ) { return 0; } + n->work = (TableNodeWork *)grown; + } + entry.value = value; entry.type = type; entry.index = 0; entry.close = 0; entry.slot = NULL; + n->work[n->work_count++] = entry; return 1; +} + +static SCHEMA_UNUSED void table_number_dispose( TableNumbering * n ) +{ +#ifdef SCHEMA_TABLE_SEQUENCE_RUNTIME + table_sequence_orders_release(n->allocator,n->orders); +#endif + table_release(n->allocator,n->entries); table_release(n->allocator,n->slots); table_release(n->allocator,n->work); + memset(n,0,sizeof(*n)); +} + +static SCHEMA_UNUSED int table_number_build( TableNumbering * n, const TableCtx * ctx, + const void * root, const TableNodeType * type, TableAllocator allocator ) +{ + memset(n,0,sizeof(*n)); n->ctx = ctx; n->allocator=allocator; + if ( root == NULL || !table_number_edge(n,root,type) ) { return 0; } + while ( n->work_count ) + { + TableNodeWork visit = n->work[--n->work_count]; + uint64_t index, at, first, last; + TableNodeEntry entry; + if ( visit.close ) { n->entries[visit.index].open = 0; continue; } + index = table_number_find(n,visit.value); + if ( index ) + { + if ( n->entries[index-1].open || n->entries[index-1].type->id != visit.type->id ) { return 0; } + continue; + } + if ( n->slot_capacity == 0 || n->count >= n->slot_capacity/2 ) + { if ( !table_number_rehash(n) ) { return 0; } } + if ( n->count == n->capacity ) + { + void * grown = table_number_grow(n->allocator,n->entries,&n->capacity,sizeof(TableNodeEntry)); + if ( grown == NULL ) { return 0; } + n->entries = (TableNodeEntry *)grown; + } + entry.value = visit.value; entry.type = visit.type; entry.packed_offset = 0; entry.open = 1; + visit.index = n->count; visit.close = 1; + n->entries[n->count++] = entry; + at = table_number_hash(visit.value) & (n->slot_capacity-1); + while ( n->slots[at] ) { at = (at+1) & (n->slot_capacity-1); } + n->slots[at] = n->count; + if ( n->work_count == n->work_capacity ) + { + void * grown = table_number_grow(n->allocator,n->work,&n->work_capacity,sizeof(TableNodeWork)); + if ( grown == NULL ) { return 0; } + n->work = (TableNodeWork *)grown; + } + n->work[n->work_count++] = visit; + first = n->work_count; + if ( !visit.type->edges(n,visit.value) ) { return 0; } + last = n->work_count; + // Callbacks emit edges in declaration order. Reverse only this set + // so the LIFO work stack visits its first edge first, before siblings. + while ( first < last && first < --last ) + { + TableNodeWork swap = n->work[first]; + n->work[first++] = n->work[last]; n->work[last] = swap; + } + } + table_release(n->allocator,n->work); n->work = NULL; n->work_capacity = 0; + return 1; +} + + +static SCHEMA_UNUSED const void * table_ref_at( const TableCtx * ctx, const TableRef * slot ) +{ + if ( slot->value == 0 ) { return NULL; } + if ( ctx != NULL && ctx->arena != NULL ) { return table_arena_at(ctx->arena,(uint32_t)slot->value); } + return (const uint8_t *)(const void *)slot + slot->value; +} + +static SCHEMA_UNUSED int table_number_slot( TableNumbering * n, const TableRef * slot, const TableNodeType * type ) +{ + const void * value = table_ref_at(n->ctx,slot); + if ( value == NULL ) { return 1; } + if ( !table_number_edge(n,value,type) ) { return 0; } + n->work[n->work_count-1].slot = slot; + return 1; +} + +static SCHEMA_UNUSED int table_graph_records( TableWriter * w ) +{ + uint64_t i; + const TableNumbering * n = w->nodes; + table_writer_leb(w,n->count-1); + for ( i=1; icount; i++ ) + { + const TableNodeEntry * entry=&n->entries[i]; + table_writer_id(w,entry->type->id); + if ( w->buffer == NULL ) + { + int64_t begin=w->offset; + if ( !entry->type->save(w,entry->value) ) { return 0; } + table_writer_leb(w,(uint64_t)(w->offset-begin)); + } + else + { + TableWriter probe=table_writer_probe(w); + if ( !entry->type->save(&probe,entry->value) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb(w,(uint64_t)probe.offset); + if ( !entry->type->save(w,entry->value) ) { return 0; } + } + } + return !w->overflow; +} + +static SCHEMA_UNUSED int64_t table_graph_save( const TableCtx * ctx, const void * root, + const TableNodeType * type, uint8_t * buffer, int64_t capacity, TableAllocator allocator ) +{ + TableNumbering numbering; + TableWriteIds vocabulary; + TableWriter w=table_writer_make(buffer,capacity,&vocabulary); + int64_t result=-1; + if ( !table_number_build(&numbering,ctx,root,type,allocator) ) { table_number_dispose(&numbering); return -1; } + w.nodes=&numbering; + table_writer_put8(&w,1); + if ( !type->save(&w,root) ) { goto done; } + w.offset--; // the root terminator follows the numbering's final field + if ( numbering.count>1 ) + { + table_writer_id(&w,UINT64_MAX); table_writer_put8(&w,12); + if ( buffer == NULL ) + { + int64_t begin=w.offset; + if ( !table_graph_records(&w) ) { goto done; } + table_writer_leb(&w,(uint64_t)(w.offset-begin)); + } + else + { + TableWriter probe=table_writer_probe(&w); + if ( !table_graph_records(&probe) ) { goto done; } + table_writer_rewind(&probe); table_writer_leb(&w,(uint64_t)probe.offset); + if ( !table_graph_records(&w) ) { goto done; } + } + } + table_writer_put8(&w,0); table_writer_finish(&w); + if ( !w.overflow ) { result=w.offset; } +done: + table_number_dispose(&numbering); return result; +} + +static SCHEMA_UNUSED uint8_t * table_graph_pack( const TableCtx * ctx, const void * root, + const TableNodeType * type, int64_t * bytes ) +{ + TableNumbering n; + uint64_t i; + int64_t total=0; + uint8_t * packed=NULL; + *bytes=0; + if ( !table_number_build(&n,ctx,root,type,ctx!=NULL && ctx->arena!=NULL ? ctx->arena->allocator : table_default_allocator()) ) { table_number_dispose(&n); return NULL; } + for ( i=0; iINT64_MAX-total ) { goto done; } + n.entries[i].packed_offset=(uint64_t)total; total+=size; + } + if ( (uint64_t)total>SIZE_MAX ) { goto done; } + packed=(uint8_t *)table_allocate(n.allocator,total); + if ( packed==NULL ) { goto done; } + memset(packed,0,(size_t)total); + for ( i=0; ipacked_offset; + n.pack_base=packed; + if(entry->type->blob) { + memcpy(target,entry->value,(size_t)(8+(int64_t)((const TableBlob *)entry->value)->length)); + } else { + const uint16_t one=1; + int order=*(const uint8_t *)(const void *)&one ? 1 : 2; + int64_t at=0; + if(!entry->type->cook_body(&n,target,entry->value,order) || + (entry->type->has_extent && !entry->type->cook_extent(&n,entry->value,target,target+table_align_up64(entry->type->size),&at,order))) { + table_release(n.allocator,packed); packed=NULL; goto done; + } + } + } + *bytes=total; +done: + table_number_dispose(&n); return packed; +} +typedef struct TableNodeDirEntry { uint64_t offset, type_id; } TableNodeDirEntry; +typedef struct TableNodeMap +{ + uint8_t * base; + const TableNodeDirEntry * entries; + uint64_t count; + int good, arena, refused; + TableSink * sink; +} TableNodeMap; + +static SCHEMA_UNUSED void table_node_resolve( const TableNodeMap * nodes, TableRef * slot, + uint64_t index, uint64_t target, TableReport * report ) +{ + const TableNodeDirEntry * entry; + slot->value = 0; + if ( index == 0 || nodes == NULL || !nodes->good ) { return; } + if ( index > nodes->count ) { report->malformed = 1; return; } + entry = &nodes->entries[index-1]; + if ( entry->offset == UINT64_MAX ) { return; } + if ( entry->type_id != target ) { report->kind_mismatch++; return; } + slot->value = nodes->arena ? (int64_t)entry->offset + : (int64_t)((nodes->base + entry->offset) - (uint8_t *)(void *)slot); +} + +typedef struct TableNodeScan +{ + TableReader fields, payload; + uint64_t declared, records; + int opened, present, malformed; +} TableNodeScan; + +static SCHEMA_UNUSED TableNodeScan table_node_scan_begin( const TableReader * root ) +{ + TableNodeScan scan; + memset(&scan,0,sizeof(scan)); scan.fields = *root; scan.fields.offset = 0; + return scan; +} + +static SCHEMA_UNUSED int table_node_scan_open( TableNodeScan * scan ) +{ + TableReader * fields = &scan->fields; + if ( scan->opened ) { return 0; } + scan->opened = 1; + for ( ;; ) + { + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb(fields,&ref) || ref == 0 || ref > fields->id_count ) { break; } + id = table_reader_id_at(fields,ref); + if ( !table_reader_has(fields,1) ) { break; } + kind = table_reader_get8(fields); + if ( id == UINT64_MAX ) + { + scan->present = 1; + if ( kind != 12 || !table_reader_span(fields,&scan->payload) ) + { scan->malformed = 1; return 0; } + } + else if ( !table_reader_skip(fields,kind) ) { break; } + } + if ( !scan->present ) { return 0; } + if ( !table_reader_leb(&scan->payload,&scan->declared) ) { scan->malformed = 1; return 0; } + return 1; +} + +static SCHEMA_UNUSED int table_node_scan_next( TableNodeScan * scan, uint64_t * type_id, TableReader * body ) +{ + uint64_t ref; + if ( !scan->opened && !table_node_scan_open(scan) ) { return 0; } + if ( scan->malformed || scan->payload.offset >= scan->payload.size ) { return 0; } + if ( !table_reader_leb(&scan->payload,&ref) || ref == 0 || ref > scan->payload.id_count || + !table_reader_span(&scan->payload,body) ) { scan->malformed = 1; return 0; } + *type_id = table_reader_id_at(&scan->payload,ref); scan->records++; + return 1; +} + +static SCHEMA_UNUSED int table_node_scan_whole( const TableNodeScan * scan ) +{ + return !scan->malformed && (!scan->present || scan->declared == scan->records); +} +#endif + #ifndef SCHEMA_GRAPHDEMO_BUILD_VERSION #define SCHEMA_GRAPHDEMO_BUILD_VERSION @@ -838,25 +1548,25 @@ static SCHEMA_UNUSED uint64_t table_cook_read64( const uint8_t * p ) refuse, and an addition that wrapped would be the defect the comparison after it was supposed to catch. Nothing past length is read on any path, including every refusing one. */ -static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align ) +static SCHEMA_UNUSED const uint8_t * table_cook_open_ex( const void * bytes, uint64_t length, uint64_t root_size, uint64_t root_align, TableRefuseReason * reason ) { const uint8_t * raw; uint64_t data_length, attribution_length, alignment, data_offset; const uint8_t * base; - if ( bytes == NULL ) { return NULL; } - if ( length < (uint64_t) table_cook_header_bytes ) { return NULL; } + if ( bytes == NULL ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } + if ( length < (uint64_t) table_cook_header_bytes ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } raw = (const uint8_t *) bytes; /* the MAGIC, bytewise and first: it is what establishes the byte order every other header word is read in, so nothing else may be read before it. A byte-reversed constant is a cook of the other order and refuses here, which is why the order never reaches a fix-up pass. */ - if ( table_cook_read64( raw ) != table_cook_magic ) { return NULL; } - if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return NULL; } - if ( table_cook_read64( raw + 8 ) != SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE ) { return NULL; } + if ( table_cook_read64( raw ) != table_cook_magic ) { return table_cook_refuse(reason,table_cook_read64(raw)==UINT64_C(0x5343484d434f4f4b) ? SCHEMA_TABLE_REFUSE_FOREIGN_ORDER : SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 16 ) != table_cook_byte_order ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_NOT_A_COOK); } + if ( table_cook_read64( raw + 8 ) != SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_WRONG_BUILD_VERSION); } /* the RESERVED words: a non-zero one means a writer used a form this build does not understand, and Open refuses rather than ignoring it. */ - if ( table_cook_read64( raw + 48 ) != 0 ) { return NULL; } - if ( table_cook_read64( raw + 56 ) != 0 ) { return NULL; } + if ( table_cook_read64( raw + 48 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } + if ( table_cook_read64( raw + 56 ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_RESERVED_NOT_ZERO); } data_length = table_cook_read64( raw + 24 ); attribution_length = table_cook_read64( raw + 32 ); alignment = table_cook_read64( raw + 40 ); @@ -866,65 +1576,1397 @@ static SCHEMA_UNUSED const uint8_t * table_cook_open( const void * bytes, uint64 puts the attribution part on an eight-byte boundary without a second padding rule) and never past the cap above; a word that is none of those rounds nothing and aligns nothing, so it is refused before it is used. */ - if ( alignment < 8 || alignment > table_cook_max_align ) { return NULL; } - if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return NULL; } + if ( alignment < 8 || alignment > table_cook_max_align ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } + if ( ( alignment & ( alignment - 1 ) ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* and it must be an alignment THE ROOT CAN SIT AT, since the root is at the region's base: both are powers of two, so "at least the root's" is one division. */ - if ( ( alignment % root_align ) != 0 ) { return NULL; } + if ( ( alignment % root_align ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_BAD_ALIGNMENT); } /* The DATA part begins at align_up( 64, alignment ). It is DERIVED and not a header field, because a fact a reader computes is a fact two writers cannot disagree about. */ data_offset = ( (uint64_t) table_cook_header_bytes + alignment - 1 ) & ~( alignment - 1 ); - if ( length < data_offset ) { return NULL; } + if ( length < data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the two part lengths against the length the caller passed. The whole file is data_offset + data_length + attribution_length, and a length that is not EXACTLY that refuses — truncation and trailing bytes are one refusal, and both terms are subtracted rather than added so no sum can carry. */ - if ( data_length > length - data_offset ) { return NULL; } - if ( attribution_length != length - data_offset - data_length ) { return NULL; } + if ( data_length > length - data_offset ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } + if ( attribution_length != length - data_offset - data_length ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } /* the ROOT sits at the region's base, so the region has to hold it: a shorter data part describes a root partly outside the file, which is the one way a match-and-point reader could hand back storage it never received. */ - if ( data_length < root_size ) { return NULL; } + if ( data_length < root_size ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_TRUNCATED); } base = raw + data_offset; /* the alignment of the BASE. The header pads the data part to the region's alignment, so a base an allocator or mmap gave you is already aligned — mmap gives page alignment for free — and a base that is not is a caller's buffer this form cannot be read out of. */ - if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return NULL; } + if ( ( (uintptr_t) base % (uintptr_t) alignment ) != 0 ) { return table_cook_refuse(reason,SCHEMA_TABLE_REFUSE_UNALIGNED_BASE); } return base; } +static SCHEMA_UNUSED const uint8_t * table_cook_open(const void * bytes,uint64_t length,uint64_t root_size,uint64_t root_align) +{ return table_cook_open_ex(bytes,length,root_size,root_align,NULL); } #endif /* SCHEMA_GRAPHDEMO_TABLE_COOK */ -/* table Stamp — TABLE-wire storage: relocatable, bounded. C has no member - initializers, so the declared defaults live in stamp_reset and nowhere - else — one definition of what a default is (docs/SPEC-TABLES.md) */ -typedef struct Stamp { - char tag[8 + 1]; /* string(8): N + 1 for the terminator the wire does not carry */ - int32_t tag_length; - int32_t seq; -} Stamp; - -/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ - -static SCHEMA_UNUSED void stamp_reset( Stamp * value ); -static SCHEMA_UNUSED void schema_graphdemo_stamp_reset_raw_( void * storage ); -static SCHEMA_UNUSED void colour_reset( Colour * value ); -static SCHEMA_UNUSED void schema_graphdemo_colour_reset_raw_( void * storage ); - -static SCHEMA_UNUSED void stamp_reset( Stamp * value ) +#ifndef SCHEMA_TABLE_COOK_WRITE_RUNTIME +#define SCHEMA_TABLE_COOK_WRITE_RUNTIME +struct TableNumbering; +typedef enum TableByteOrder { TableByteOrder_Little=1, TableByteOrder_Big=2 } TableByteOrder; +static SCHEMA_UNUSED void table_cook_put(uint8_t * at,uint64_t value,int width,int order) { - memset( value->tag, 0, sizeof( value->tag ) ); - value->tag_length = 0; - value->seq = 0; + int i; for(i=0;i> (8*(order==TableByteOrder_Little ? i : width-1-i))); } } - -static SCHEMA_UNUSED void schema_graphdemo_stamp_reset_raw_( void * storage ) { stamp_reset( (Stamp *) storage ); } - +static SCHEMA_UNUSED void table_cook_put128(uint8_t * at,uint64_t lo,uint64_t hi,int order) +{ + table_cook_put(at,order==TableByteOrder_Little ? lo : hi,8,order); + table_cook_put(at+8,order==TableByteOrder_Little ? hi : lo,8,order); +} +static SCHEMA_UNUSED void table_cook_bytes(uint8_t * at,const void * source,int64_t used,int64_t capacity) +{ if(used>0) { memcpy(at,source,(size_t)(used=0 && bytes<=INT64_MAX/8 ? bytes*8 : 0,0}; return r; } +static SCHEMA_UNUSED int table_bit_has(const TableBitReader * r,int64_t n) +{ return n>=0 && r->offset>=0 && r->offset<=r->bits && n<=r->bits-r->offset; } +static SCHEMA_UNUSED void table_bit_put(TableBitWriter * w,uint64_t value,int64_t n) +{ + int64_t index,bit,need,i; uint64_t word,head; + if(n<0 || n>64 || w->bits>INT64_MAX-n-7) { w->overflow=1;return; } + if(n==0) return; + if(w->capacity<0 || (w->bits+n+7)/8>w->capacity) {w->overflow=1;w->bits+=n;return;} + if(w->buffer==NULL) {w->bits+=n;return;} + if(n<64) value&=(UINT64_C(1)<bits>>3;bit=w->bits&7;need=(bit+n+7)>>3; + head=bit ? (uint64_t)w->buffer[index]&((UINT64_C(1)<buffer[index+i]=(uint8_t)(word>>(8*i)); + if(need>8) w->buffer[index+8]=(uint8_t)(value>>(64-bit)); + w->bits+=n; +} +static SCHEMA_UNUSED int table_bit_get(TableBitReader * r,uint64_t * value,int64_t n) +{ + int64_t got=0; + if(n>64 || !table_bit_has(r,n)) return 0; + *value=0; + while(gotoffset&7,take=8-bit; + if(take>n-got) take=n-got; + *value|=(((uint64_t)r->buffer[r->offset>>3]>>bit)&((UINT64_C(1)<offset+=take;got+=take; + } + return 1; +} +static SCHEMA_UNUSED int table_bit_skip(TableBitReader * r,int64_t n) +{ if(!table_bit_has(r,n))return 0;r->offset+=n;return 1; } +static SCHEMA_UNUSED void table_bit_align_write(TableBitWriter * w) +{table_bit_put(w,0,(8-(w->bits&7))&7);} +static SCHEMA_UNUSED int table_bit_align_read(TableBitReader * r) +{uint64_t pad;return table_bit_get(r,&pad,(8-(r->offset&7))&7) && pad==0;} +static SCHEMA_UNUSED void table_bit_putbytes(TableBitWriter * w,const void * source,int64_t n) +{ + int64_t i; + if(n<0 || n>(INT64_MAX-w->bits)/8) {w->overflow=1;return;} + if(w->buffer==NULL || (w->bits&7)==0) { + if(w->bits/8+n>w->capacity)w->overflow=1; + else if(w->buffer!=NULL && n>0)memcpy(w->buffer+w->bits/8,source,(size_t)n); + w->bits+=n*8;return; + } + for(i=0;iINT64_MAX/8 || !table_bit_has(r,n*8))return 0; + if((r->offset&7)==0){if(n>0)memcpy(target,r->buffer+r->offset/8,(size_t)n);r->offset+=n*8;return 1;} + for(i=0;i 0 ) { n++; span >>= 1; } + return n; +} + +// table_message_kind_bits is the widest RANGED value a kind can carry, its own +// storage width: a width above it is a hostile width on the announcement. +static SCHEMA_UNUSED int64_t table_message_kind_bits( uint8_t kind ) +{ + switch ( kind ) + { + case 2: case 6: case 20: case 25: return 8; + case 3: case 7: case 21: case 26: return 16; + case 4: case 8: case 22: case 27: return 32; + case 5: case 9: case 23: case 28: return 64; + default: return 128; + } +} + +// table_message_quantization is SPEC.md §4.3's derivation over an announced +// triple, in float32 and by nothing else: delta, the step count and the +// width. False is a triple SPEC.md calls non-conforming, which on the +// announcement is a hostile width like any other (§3.3). +static SCHEMA_UNUSED int table_message_quantization( float qmin, float qmax, float qres, float * delta, uint32_t * count, int64_t * bits ) +{ + if ( !( qmin < qmax ) || !( qres > 0.0f ) ) { return 0; + } + (*delta) = qmax - qmin; + float values = (*delta) / qres; + if ( !( (*delta) - (*delta) == 0.0f ) || !( values - values == 0.0f ) ) { return 0; + } // Inf - Inf is NaN + if ( !( values >= 1.0f ) ) { values = 1.0f; + } + else if ( values > 4294967040.0f ) { values = 4294967040.0f; + } // the largest float below 2^32 + (*count) = (uint32_t) values; + if ( (float) (*count) < values ) { (*count)++; + } // ceil, on a value the cast holds exactly + (*bits) = table_bits_required( 0, (int64_t) (*count) ); + return 1; +} + +// The two roundings on each side of the rule (SPEC.md §7.2): the product +// rounds to float32 BEFORE the add, which a compiler permitted to contract +// would otherwise fuse into one rounding and move the wire. +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __aarch64__ ) || defined( _M_ARM64 ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+w" ( x ) ) +#elif ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __x86_64__ ) || defined( __i386__ ) ) +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) __asm__ ( "" : "+x" ( x ) ) +#else +#define SCHEMA_TABLE_FLOAT_FORCE_ROUND( x ) do { volatile float schema_table_float_force_round_slot_ = ( x ); ( x ) = schema_table_float_force_round_slot_; } while ( 0 ) +#endif + +// table_message_quantize is the writer's half: the index a value takes. +static SCHEMA_UNUSED uint32_t table_message_quantize( float value, float qmin, float delta, uint32_t count ) +{ + float normalized = ( value - qmin ) / delta; + if ( !( normalized >= 0.0f ) ) { normalized = 0.0f; + } + else if ( !( normalized <= 1.0f ) ) { normalized = 1.0f; + } + float scaled = normalized * (float) count; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + uint32_t index = (uint32_t) ( scaled + 0.5f ); + // floor of a non-negative value + if ( index > count ) { index = count; + } + return index; +} + +// table_message_dequantize is the reader's half: the float an index names. +static SCHEMA_UNUSED float table_message_dequantize( uint32_t index, float qmin, float delta, uint32_t count ) +{ + if ( index > count ) { index = count; + } + const float normalized = index / (float) count; + float scaled = normalized * delta; + SCHEMA_TABLE_FLOAT_FORCE_ROUND( scaled ); + return scaled + qmin; +} + +static SCHEMA_UNUSED int table_message_integer_kind( uint8_t kind ) +{ + return ( kind >= 2 && kind <= 9 ) || kind == 18 || kind == 19; +} + +static SCHEMA_UNUSED int table_message_fixed_kind( uint8_t kind ) { return kind >= 20 && kind <= 29; +} + +static SCHEMA_UNUSED int table_message_known_kind( uint8_t kind ) +{ + return kind == 0 || ( kind >= 1 && kind <= 17 ) || ( kind >= 18 && kind <= 29 ) || ( kind >= 30 && kind <= 33 ); +} + + +typedef struct TableMessageEntry { + uint64_t id; + int64_t min,max,base_lo,base_hi,elem_max,elem_base_lo,elem_base_hi; + float qmin,qdelta;uint32_t qcount;float elem_qmin,elem_qdelta;uint32_t elem_qcount; + int16_t value_bits,elem_value_bits; + uint8_t kind,packing,elem_kind,elem_packing; +} TableMessageEntry; +typedef struct TableMessageShapeFacts { + uint8_t packing,elem_kind; int64_t value_bits,base_lo,base_hi,min,max; + float qmin,qmax,qres,qdelta;uint32_t qcount; +} TableMessageShapeFacts; +typedef struct TableVocabulary { + TableMessageEntry * entries;int64_t max_entries,count,ref_bits; + uint64_t build_version;int announced,refused;int64_t max_bytes; +} TableVocabulary; +enum { SCHEMA_TABLE_NO_VOCABULARY=3,SCHEMA_TABLE_SECOND_ANNOUNCEMENT=4,SCHEMA_TABLE_VOCABULARY_TOO_LARGE=5,SCHEMA_TABLE_BATCH_TOO_LARGE=6 }; +static SCHEMA_UNUSED TableVocabulary table_vocabulary(TableMessageEntry * storage,int64_t capacity) +{TableVocabulary v;memset(&v,0,sizeof(v));v.entries=storage;v.max_entries=capacity;v.max_bytes=65536;return v;} +static SCHEMA_UNUSED int table_message_leb(const uint8_t * in,int64_t size,int64_t * at,uint64_t * value) +{ + TableReader r=table_reader_make(in,size,NULL); + r.offset=*at; + if(!table_reader_leb(&r,value))return 0; + *at=r.offset; + return 1; +} +static SCHEMA_UNUSED int64_t table_message_value_bits(uint8_t kind,uint8_t packing,int64_t bits) +{ + if(kind==1)return 1; + if(kind==11)return 64; + if(kind==10)return packing==2?bits:32; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind))return packing==1?bits:table_message_kind_bits(kind); + return -1; +} +static SCHEMA_UNUSED int table_message_shape_read(const uint8_t * in,int64_t size,int64_t * at,uint8_t kind,TableMessageShapeFacts * f) +{ + uint64_t v=0; + int i,k; + if(table_message_integer_kind(kind)||table_message_fixed_kind(kind)||kind==10){ + if(*at>=size)return 0; + f->packing=in[(*at)++]; + if(f->packing==0)return 1; + if(f->packing==1 && kind!=10){ + if(!table_message_leb(in,size,at,&v)||v>(uint64_t)table_message_kind_bits(kind))return 0; + f->value_bits=(int64_t)v; + if(kind==18||kind==19||table_message_fixed_kind(kind)){ + uint64_t lo=0,hi=0; + if(*at>size-16)return 0; + for(i=0;i<8;i++){lo|=(uint64_t)in[*at+i]<<(8*i); + hi|=(uint64_t)in[*at+8+i]<<(8*i); + } + f->base_lo=(int64_t)lo; + f->base_hi=(int64_t)hi; + *at+=16; + return 1; + } + if(!table_message_leb(in,size,at,&v))return 0; + f->base_lo=kind>=2&&kind<=5?(int64_t)(v>>1)^-(int64_t)(v&1):(int64_t)v; + return 1; + } + if(f->packing==2 && kind==10){ + uint32_t raw[3]={0,0,0}; + if(*at>size-12)return 0; + for(k=0;k<3;k++)for(i=0;i<4;i++)raw[k]|=(uint32_t)in[*at+4*k+i]<<(8*i); + *at+=12; + memcpy(&f->qmin,raw,4); + memcpy(&f->qmax,raw+1,4); + memcpy(&f->qres,raw+2,4); + return table_message_quantization(f->qmin,f->qmax,f->qres,&f->qdelta,&f->qcount,&f->value_bits); + }return 0; + } + if(kind==12||kind==33){if(!table_message_leb(in,size,at,&v)||v>INT32_MAX)return 0; + f->max=(int64_t)v; + return 1; + } + if(kind==14||kind==16){ + if(kind==14){if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX)return 0; + f->min=(int64_t)v; + } + if(!table_message_leb(in,size,at,&v)||v>UINT32_MAX||v<(uint64_t)f->min)return 0; + f->max=(int64_t)v; + if(*at>=size)return 0; + f->elem_kind=in[(*at)++]; + return table_message_known_kind(f->elem_kind)&&f->elem_kind!=12&&f->elem_kind!=33; + }return 1; +} +static SCHEMA_UNUSED int table_message_entry_read(const uint8_t * in,int64_t size,int64_t * at,TableMessageEntry * e) +{ + TableMessageShapeFacts own={0},elem={0}; + int i; + if(*at<0||*at>size-9)return 0; + memset(e,0,sizeof(*e)); + e->value_bits=e->elem_value_bits=-1; + for(i=0;i<8;i++)e->id|=(uint64_t)in[*at+i]<<(8*i); + e->kind=in[*at+8]; + *at+=9; + if(!table_message_known_kind(e->kind)||!table_message_shape_read(in,size,at,e->kind,&own))return 0; + e->packing=own.packing; + e->value_bits=(int16_t)table_message_value_bits(e->kind,own.packing,own.value_bits); + e->min=own.min; + e->max=own.max; + e->base_lo=own.base_lo; + e->base_hi=own.base_hi; + e->qmin=own.qmin; + e->qdelta=own.qdelta; + e->qcount=own.qcount; + e->elem_kind=own.elem_kind; + if(e->kind==14||e->kind==16){ + if(!table_message_shape_read(in,size,at,e->elem_kind,&elem))return 0; + e->elem_packing=elem.packing; + e->elem_value_bits=(int16_t)table_message_value_bits(e->elem_kind,elem.packing,elem.value_bits); + e->elem_max=elem.max; + e->elem_base_lo=elem.base_lo; + e->elem_base_hi=elem.base_hi; + e->elem_qmin=elem.qmin; + e->elem_qdelta=elem.qdelta; + e->elem_qcount=elem.qcount; + }return 1; +} + + +static SCHEMA_UNUSED int table_announce_once(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReader r; + int seen_version=0,seen_words=0; + const uint8_t * words=NULL; + int64_t words_bytes=0,at=0,count=0,nodes=0,touched=0; + if(!table_wire_open(&r,buffer,bytes,report))return 0; + for(;;){ + uint64_t ref,id; + uint8_t kind; + if(!table_reader_leb(&r,&ref))goto malformed; + if(ref==0)break; + if(ref>r.id_count||!table_reader_has(&r,1))goto malformed; + id=table_reader_id_at(&r,ref); + kind=table_reader_get8(&r); + if(id==UINT64_C(0xfffffffffffffffe)){ + if(kind!=9||!table_reader_has(&r,8))goto malformed; + v->build_version=table_reader_get64(&r); + seen_version++; + continue; + } + if(id==UINT64_C(0xfffffffffffffffd)){ + TableReader field; + uint64_t n; + if(kind!=14||!table_reader_span(&r,&field)||!table_reader_has(&field,1)||table_reader_get8(&field)!=6)goto malformed; + if(!table_reader_leb(&field,&n)||!table_reader_room(&field,n)||n!=(uint64_t)(field.size-field.offset))goto malformed; + if(v->max_bytes<0 || n>(uint64_t)v->max_bytes){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + return 0; + } + words=field.buffer+field.offset; + words_bytes=(int64_t)n; + seen_words++; + continue; + } + report->unknown++; + if(!table_reader_skip(&r,kind))goto malformed; + } + if(seen_version!=1||seen_words!=1||r.offset!=r.size)goto malformed; + while(atentries==NULL){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + goto refused; + } + if(count>=v->max_entries){report->refused=1; + report->reason=SCHEMA_TABLE_VOCABULARY_TOO_LARGE; + goto refused; + } + entry=&v->entries[count]; + touched=count+1; + if(!table_message_entry_read(words,words_bytes,&at,entry))goto malformed; + if(entry->id==UINT64_C(0xfffffffffffffffe)||entry->id==UINT64_C(0xfffffffffffffffd))goto malformed; + if(entry->id==UINT64_MAX && nodes++>0)goto malformed; + /* Declared canonical bytes identify a shape. Resolved quantization facts + alone can collapse different triples (#722). Until validation finishes, + min/max temporarily hold the source span; no announcement pointer escapes. */ + for(i=0;ientries[i].min,length=v->entries[i].max-start; + if(length==at-began && !memcmp(words+start,words+began,(size_t)length))goto malformed; + } + entry->min=began; + entry->max=at; + count++; + } + /* Resolve the accepted spans into the caller's final 96-byte entries. */ + at=0; + {int64_t i; + for(i=0;ientries+i))goto malformed; + } + v->count=count; + v->ref_bits=table_bits_required(0,count); + v->announced=1; + return 1; +malformed: + report->malformed=1; +refused: + if(touched)memset(v->entries,0,(size_t)touched*sizeof(*v->entries)); + return 0; +} +static SCHEMA_UNUSED int announce_read(TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + int ok; + if(report==NULL)report=&ignored; + if(v->announced||v->refused){report->refused=1; + report->reason=SCHEMA_TABLE_SECOND_ANNOUNCEMENT; + return 0; + } + ok=table_announce_once(v,buffer,bytes,report); + if(!ok)v->refused=1; + return ok; +} + +typedef struct TableMessageReader { + TableBitReader bits; const TableVocabulary * vocabulary; TableReport * report; + int64_t index_bits; int extent_refused;TableNodeMap * nodes; +} TableMessageReader; +static SCHEMA_UNUSED int table_message_ref(TableMessageReader * r,const TableMessageEntry ** entry,int name) +{ + uint64_t ref=0; + *entry=NULL; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref==0)return 1; + if(ref>(uint64_t)r->vocabulary->count)return 0; + *entry=r->vocabulary->entries+ref-1; + return (*entry)->idkind==0); +} +static SCHEMA_UNUSED TableMessageEntry table_message_element(const TableMessageEntry * e) +{ + TableMessageEntry v; + memset(&v,0,sizeof(v)); + v.kind=e->elem_kind; + v.packing=e->elem_packing; + v.value_bits=e->elem_value_bits; + v.base_lo=e->elem_base_lo; + v.base_hi=e->elem_base_hi; + v.max=e->elem_max; + v.qmin=e->elem_qmin; + v.qdelta=e->elem_qdelta; + v.qcount=e->elem_qcount; + return v; +} +static SCHEMA_UNUSED int table_message_skip_run(TableMessageReader * r,uint64_t count,int64_t width) +{ + if(width<0)return 0; + if(width==0)return 1; + if(count>(uint64_t)(INT64_MAX/width))return 0; + return table_bit_skip(&r->bits,(int64_t)(count*(uint64_t)width)); +} +static SCHEMA_UNUSED int table_message_count(TableMessageReader * r,const TableMessageEntry * e,uint64_t * n) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,table_bits_required(e->min,e->max)))return 0; + *n=raw+(uint64_t)e->min; + return !(e->kind==14 && e->elem_kind==6) || table_bit_align_read(&r->bits); +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader *,const TableMessageEntry *); +static SCHEMA_UNUSED int table_message_skip_body(TableMessageReader * r) +{ + for(;;) {const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))return 0; + if(entry==NULL)return 1; + if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int table_message_skip(TableMessageReader * r,const TableMessageEntry * e) +{ + uint64_t n,i; + const TableMessageEntry * entry; + switch(e->kind) { + case 0:case 32:return 1; + case 30:return table_message_ref(r,&entry,1); + case 13:return table_message_skip_body(r); + case 17:return r->index_bits>0 && table_bit_skip(&r->bits,r->index_bits); + case 15: + if(!table_message_ref(r,&entry,0))return 0; + return entry==NULL || (entry->kind!=0 && table_message_skip(r,entry)); + case 12:case 33: + if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==12 && !table_bit_align_read(&r->bits))return 0; + return table_message_skip_run(r,n,e->kind==12?8:16); + case 31: + if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + return table_message_skip_run(r,n,8); + case 14:case 16: { + TableMessageEntry elem=table_message_element(e); + if(!table_message_count(r,e,&n))return 0; + if(e->kind==14 && (elem.kind==0||elem.kind==32))return 1; + if(e->kind==14 && elem.value_bits>=0)return table_message_skip_run(r,n,elem.value_bits); + for(i=0;ikind==16 && !table_message_ref(r,&entry,1))return 0; + if(!table_message_skip(r,&elem))return 0; + }return 1; + } + default:return e->value_bits>=0 && table_bit_skip(&r->bits,e->value_bits); + } +} +static SCHEMA_UNUSED int table_message_number(TableMessageReader * r,const TableMessageEntry * e,uint64_t * lo,uint64_t * hi) +{ + int64_t width=e->value_bits; + uint64_t before; + *lo=*hi=0; + if(width<0 || width>128 || !table_bit_get(&r->bits,lo,width<64?width:64))return 0; + if(width>64 && !table_bit_get(&r->bits,hi,width-64))return 0; + if(e->packing==1) { + before=*lo; + *lo+=(uint64_t)e->base_lo; + if(table_message_kind_bits(e->kind)>64)*hi+=(uint64_t)e->base_hi+(*lokind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23)) && width>0 && width<64 && (*lo&(UINT64_C(1)<<(width-1)))) { + *lo|=UINT64_MAX<kind>=2 && e->kind<=5)||(e->kind>=20 && e->kind<=23))*hi=(int64_t)*lo<0?UINT64_MAX:0; + return 1; +} +static SCHEMA_UNUSED int table_message_float(TableMessageReader * r,const TableMessageEntry * e,float * value) +{ + uint64_t raw; + if(!table_bit_get(&r->bits,&raw,e->value_bits))return 0; + if(e->packing==2) { + if(raw>e->qcount)return 0; + *value=table_message_dequantize((uint32_t)raw,e->qmin,e->qdelta,e->qcount); + }else *value=table_bits_to_float((uint32_t)raw); + return 1; +} +static SCHEMA_UNUSED int64_t table_message_open(TableMessageReader * r,const TableVocabulary * v,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + uint64_t count; + memset(r,0,sizeof(*r)); + r->report=report; + r->vocabulary=v; + if(bytes<1 || buffer==NULL){report->malformed=1; + return -1; + } + if(buffer[0]!=2){report->refused=1; + report->reason=SCHEMA_TABLE_NEWER_FORM; + return -1; + } + if(v==NULL || !v->announced){report->refused=1; + report->reason=SCHEMA_TABLE_NO_VOCABULARY; + return -1; + } + r->bits=table_bit_reader(buffer+1,bytes-1); + if(!table_bit_get(&r->bits,&count,8)){report->malformed=1; + return -1; + } + return (int64_t)count+1; +} +static SCHEMA_UNUSED int table_message_close(TableMessageReader * r) +{ + if(!table_bit_align_read(&r->bits)||r->bits.offset!=r->bits.bits){r->report->malformed=1; + return 0; + }return 1; +} + +static SCHEMA_UNUSED int table_message_nodes_open(TableMessageReader * r,uint64_t * count) +{ + uint64_t ref; + int64_t start=r->bits.offset; + *count=0; + if(!table_bit_get(&r->bits,&ref,r->vocabulary->ref_bits))return 0; + if(ref>(uint64_t)r->vocabulary->count)return 0; + if(ref==0 || r->vocabulary->entries[ref-1].id!=UINT64_MAX){r->bits.offset=start; + r->index_bits=1; + return 1; + } + if(!table_bit_get(&r->bits,count,32))return 0; + r->index_bits=table_bits_required(0,(int64_t)*count+1); + /* Every record spends at least its type reference. Bound the walk before + inspecting any record, even when its body holds no scalar bits. */ + return r->vocabulary->ref_bits>0 && *count<=(uint64_t)((r->bits.bits-r->bits.offset)/r->vocabulary->ref_bits); +} +static SCHEMA_UNUSED int table_message_blob_scan(TableMessageReader * r,uint64_t * length) +{ + return table_bit_get(&r->bits,length,32) && table_bit_align_read(&r->bits) && table_message_skip_run(r,*length,8); +} + +#endif + +#ifndef SCHEMA_TABLE_COOK_GRAPH_RUNTIME +#define SCHEMA_TABLE_COOK_GRAPH_RUNTIME +static SCHEMA_UNUSED int table_cook_ref(TableNumbering * n,uint8_t * at,const TableRef * slot,int order) +{ + const void * pointee=table_ref_at(n->ctx,slot); + uint64_t index=table_number_find(n,pointee); + if(pointee!=NULL && index==0) { return 0; } + table_cook_put(at,index ? n->entries[index-1].packed_offset-(uint64_t)(at-n->pack_base) : 0,8,order); + return 1; +} +static SCHEMA_UNUSED int64_t table_cook_layout(TableNumbering * n,int64_t * alignment,int64_t * data_bytes) +{ + uint64_t i; + int64_t total=0, align=8; + for(i=0;icount;i++) { + TableNodeEntry * entry=&n->entries[i]; const TableNodeType * type=entry->type; + int64_t size=type->size, node_align=type->alignment; + if(type->blob) { size=8+(int64_t)((const TableBlob *)entry->value)->length+(type->blob==2); } + else if(type->has_extent) { + int64_t extent=0; + if(!type->cook_extent(n,entry->value,NULL,NULL,&extent,TableByteOrder_Little) || size>INT64_MAX-7) { return -1; } + size=(size+7)&~INT64_C(7); if(extent>INT64_MAX-size) { return -1; } size+=extent; + } + if(node_align<1 || total>INT64_MAX-(node_align-1)) { return -1; } + total=(total+node_align-1)&~(node_align-1); + entry->packed_offset=(uint64_t)total; + if(size<0 || size>INT64_MAX-total) { return -1; } total+=size; + if(node_align>align) { align=node_align; } + } + if(total>INT64_MAX-(align-1)) { return -1; } total=(total+align-1)&~(align-1); + *alignment=align; *data_bytes=total; + if(total>INT64_MAX-64 || n->count>(uint64_t)(INT64_MAX-64-total)/16) { return -1; } + return 64+total+(int64_t)n->count*16; +} +static SCHEMA_UNUSED int64_t table_graph_cook(const TableCtx * ctx,const void * root,const TableNodeType * type, + void * output,uint64_t capacity,int order,TableAllocator allocator,uint64_t version,int measure) +{ + TableNumbering n; int64_t result=-1,align=8,bytes=0,need; uint64_t i; uint8_t * raw=(uint8_t *)output; + if(!table_number_build(&n,ctx,root,type,allocator)) { table_number_dispose(&n); return -1; } + need=table_cook_layout(&n,&align,&bytes); if(need<0) { goto done; } + if(measure) { result=need; goto done; } + if(raw==NULL || (uint64_t)need>capacity || (uint64_t)need>SIZE_MAX) { goto done; } + memset(raw,0,(size_t)need); n.pack_base=raw+64; + for(i=0;ipacked_offset; + if(entry->type->blob) { + const TableBlob * blob=(const TableBlob *)entry->value; + table_cook_put(at,blob->length,4,order); table_cook_bytes(at+8,blob+1,blob->length,blob->length); + } else { + int64_t extent=0; + if(!entry->type->cook_body(&n,at,entry->value,order)) { goto done; } + if(entry->type->has_extent) { + int64_t expected=0,record=(entry->type->size+7)&~INT64_C(7); + if(!entry->type->cook_extent(&n,entry->value,NULL,NULL,&expected,order) || + !entry->type->cook_extent(&n,entry->value,at,at+record,&extent,order) || extent!=expected) { goto done; } + } + } + table_cook_put(n.pack_base+bytes+i*16,entry->packed_offset,8,order); + table_cook_put(n.pack_base+bytes+i*16+8,entry->type->id,8,order); + } + /* A failed body must never leave a recognizable cook header. */ + table_cook_header(raw,version,bytes,(int64_t)n.count,align,order); result=need; +done: + table_number_dispose(&n); return result; +} +#endif + +#ifndef SCHEMA_TABLE_RETAIN_RUNTIME +#define SCHEMA_TABLE_RETAIN_RUNTIME +#define SCHEMA_TABLE_RETAIN_DEPTH_MAX 1 +#define SCHEMA_TABLE_RETAIN_WALK_MAX 64 +#define SCHEMA_TABLE_RETAIN_HEADER 26 + +typedef struct TableRetainId { uint64_t id; int32_t slot; } TableRetainId; +typedef struct TableRetain { + uint8_t * bytes; int64_t capacity,used; + TableRetainId * ids; int32_t id_capacity,id_used,count; + const uint8_t * base; const TableNodeDirEntry * directory; int64_t directory_count; +} TableRetain; +typedef struct TableRetainStep {uint32_t ordinal,index;} TableRetainStep; +typedef struct TableRetainPath { + const void * at; uint32_t node; int32_t depth; + TableRetainStep steps[SCHEMA_TABLE_RETAIN_DEPTH_MAX]; +} TableRetainPath; +typedef struct TableRetainIds { + TableWriteIds known; + int32_t slots[sizeof(((TableWriteIds *)0)->ids)/sizeof(uint64_t)]; + TableRetain * retain; int32_t count; int lost; +} TableRetainIds; +typedef struct TableRetainWalk {TableRetain * retain;TableRetainIds * ids;TableRetainPath path;} TableRetainWalk; +static const uint64_t table_retain_known[]={UINT64_C(0x0a8f12cc5f9a0c03),UINT64_C(0x1e4984ef2e958a4c),UINT64_C(0x24b070ada2041cb0),UINT64_C(0x24f3a319b88552c1),UINT64_C(0x2f2ec0474f1c4fe4),UINT64_C(0x327fe6dc702553fd),UINT64_C(0x39f7fcec8fcb623d),UINT64_C(0x3bf8fbbad1587cdd),UINT64_C(0x4320e9a2e32eac38),UINT64_C(0x4339ee8ab21c8380),UINT64_C(0x4554e34a747022df),UINT64_C(0x4a9a31623ab5f213),UINT64_C(0x509220bb65a646b7),UINT64_C(0x56d7ab194448a4f3),UINT64_C(0x5b25b8ef511eb395),UINT64_C(0x60839e2395be697e),UINT64_C(0x69ff34904242a73d),UINT64_C(0x6dadeaaee49d6d18),UINT64_C(0x704be0d8faaffc58),UINT64_C(0x732dfbcc9b0cf0bb),UINT64_C(0x75d8e97600b296ea),UINT64_C(0x76aaaa535714d805),UINT64_C(0x77af761956600b54),UINT64_C(0x7a8060916400fe66),UINT64_C(0x7ce4fd9430e80cea),UINT64_C(0x7e9d0b96d39e517d),UINT64_C(0x802517e298c70b03),UINT64_C(0x823b8a195ce2133c),UINT64_C(0x9d167ef77aed79b6),UINT64_C(0x9d8b8aa2b404c2c8),UINT64_C(0x9deeefd89ca8a81d),UINT64_C(0xae3b9113b7db93a4),UINT64_C(0xaf63da4c8601e926),UINT64_C(0xaf63df4c8601f1a5),UINT64_C(0xaf63ef4c86020cd5),UINT64_C(0xb97e90a3784c431d),UINT64_C(0xbb62c62c9808ea37),UINT64_C(0xc4bcadba8e631b86),UINT64_C(0xd6458a3eef83d457),UINT64_C(0xd858c2cb7f1514cc),UINT64_C(0xdd9eb981e152e90c),UINT64_C(0xe5316cbaa025f028),UINT64_C(0xeddcb72b15486e77),UINT64_C(0xee5f6d7b48b44de8),UINT64_C(0xee7ba9ad45c64144),UINT64_C(0xf60ec899a5a69fa9),UINT64_C(0xffffffffffffffff),}; +static SCHEMA_UNUSED uint32_t table_retain_u32(const uint8_t * p) +{return (uint32_t)p[0]|(uint32_t)p[1]<<8|(uint32_t)p[2]<<16|(uint32_t)p[3]<<24; +} +static SCHEMA_UNUSED uint64_t table_retain_u64(const uint8_t * p) +{return table_retain_u32(p)|(uint64_t)table_retain_u32(p+4)<<32; +} +static SCHEMA_UNUSED void table_retain_put32(uint8_t * p,uint32_t v) +{int i; +for(i=0;i<4;i++)p[i]=(uint8_t)(v>>(8*i)); +} +static SCHEMA_UNUSED void table_retain_put64(uint8_t * p,uint64_t v) +{table_retain_put32(p,(uint32_t)v); +table_retain_put32(p+4,(uint32_t)(v>>32)); +} +static SCHEMA_UNUSED int64_t table_retain_leb_bytes(uint64_t v) +{int64_t n=1; +while(v>=128){v>>=7; +n++; +}return n; +} +static SCHEMA_UNUSED int table_retain_reserved(uint64_t id) +{return id>=UINT64_MAX-2; +} +static SCHEMA_UNUSED TableRetainPath table_retain_root(const void * at,uint32_t node) +{TableRetainPath p; +memset(&p,0,sizeof(p)); +p.at=at; +p.node=node; +return p; +} +static SCHEMA_UNUSED TableRetainWalk table_retain_step(TableRetainWalk keep,uint32_t ordinal,uint32_t index) +{if(keep.path.depth0&&keep.path.depth<=SCHEMA_TABLE_RETAIN_DEPTH_MAX)keep.path.steps[keep.path.depth-1].index=index; +return keep; +} +static SCHEMA_UNUSED uint8_t * table_retain_payload(uint8_t * record) +{return record+SCHEMA_TABLE_RETAIN_HEADER+(int64_t)table_retain_u32(record+8)*8; +} +static SCHEMA_UNUSED const void * table_retain_at(const TableRetain * retain,const uint8_t * record) +{uint32_t node=table_retain_u32(record+4); +if(!retain->directory||!node||node>retain->directory_count)return NULL; +return retain->base+retain->directory[node-1].offset; +} +static SCHEMA_UNUSED int table_retain_under(const TableRetain * retain,const uint8_t * record,const TableRetainPath * path,int field,uint32_t ordinal) +{ + uint32_t depth=table_retain_u32(record+8); + int32_t i; + const uint8_t * steps=record+SCHEMA_TABLE_RETAIN_HEADER; + if(path->depth<0||path->depth>SCHEMA_TABLE_RETAIN_DEPTH_MAX||table_retain_at(retain,record)!=path->at)return 0; + if(depth<(uint32_t)path->depth||(field&&depth==(uint32_t)path->depth))return 0; + for(i=0;idepth;i++)if(table_retain_u32(steps+8*i)!=path->steps[i].ordinal||table_retain_u32(steps+8*i+4)!=path->steps[i].index)return 0; + return !field||table_retain_u32(steps+8*path->depth)==ordinal; +} +static SCHEMA_UNUSED int table_retain_here(const TableRetain * retain,const uint8_t * record,const TableRetainPath * path) +{return table_retain_u32(record+8)==(uint32_t)path->depth&&table_retain_under(retain,record,path,0,0); +} +static SCHEMA_UNUSED void table_retain_discard(TableRetainWalk keep,int field,uint32_t ordinal) +{ + TableRetain * r=keep.retain; + int64_t read=0,write=0; + int32_t i,kept=0; + if(!r||!r->bytes)return; + for(i=0;icount;i++){uint32_t n=table_retain_u32(r->bytes+read); + if(!table_retain_under(r,r->bytes+read,&keep.path,field,ordinal)){if(read!=write)memmove(r->bytes+write,r->bytes+read,n); + write+=n; + kept++; + }read+=n; + }r->used=write; + r->count=kept; +} +static SCHEMA_UNUSED void table_retain_reset(TableRetain * retain,const TableNodeMap * nodes,const uint8_t * region) +{if(retain){retain->used=0; +retain->id_used=0; +retain->count=0; +retain->base=region; +retain->directory=nodes?nodes->entries:NULL; +retain->directory_count=nodes?(int64_t)nodes->count:0; +}} +static SCHEMA_UNUSED int table_retain_nameable(uint64_t id) +{int64_t lo=0,hi=(int64_t)(sizeof(table_retain_known)/sizeof(table_retain_known[0]))-1; +while(lo<=hi){int64_t mid=lo+(hi-lo)/2; +if(table_retain_known[mid]==id)return 1; +if(table_retain_known[mid]retain; + TableWriteIds * v=&ids->known; + if(record&&!table_retain_nameable(id)){ + if(!r){ids->lost=1; + return 0; + }for(i=0;iid_used;i++)if(r->ids[i].id==id)return (uint64_t)r->ids[i].slot; + if(!r->ids||r->id_used>=r->id_capacity||ids->count==INT32_MAX){ids->lost=1; + return 0; + } + r->ids[r->id_used].id=id; + r->ids[r->id_used++].slot=++ids->count; + return (uint64_t)ids->count; + } + for(i=0;icount;i++)if(v->ids[i]==id)return (uint64_t)ids->slots[i]; + if(v->count==(int)(sizeof(v->ids)/sizeof(v->ids[0]))||ids->count==INT32_MAX){*overflow=1; + return 0; + } + v->ids[v->count]=id; + ids->slots[v->count++]=++ids->count; + return (uint64_t)ids->count; +} +static SCHEMA_UNUSED void table_retain_truncate(TableRetainIds * ids,int32_t mark) +{while(ids->known.count&&ids->slots[ids->known.count-1]>mark)ids->known.count--; +if(ids->retain)while(ids->retain->id_used&&ids->retain->ids[ids->retain->id_used-1].slot>mark)ids->retain->id_used--; +ids->count=mark; +} +static SCHEMA_UNUSED void table_retain_writer_id(TableWriter * w,uint64_t id,TableRetainWalk keep) +{table_writer_leb(w,table_retain_ref(keep.ids,id,0,&w->overflow)); +} +static SCHEMA_UNUSED TableWriter table_retain_probe(const TableWriter * w,TableRetainWalk keep) +{TableWriter p=*w; +p.buffer=NULL; +p.capacity=INT64_MAX; +p.offset=0; +p.overflow=0; +p.id_checkpoint=keep.ids->count; +return p; +} +static SCHEMA_UNUSED void table_retain_rewind(const TableWriter * probe,TableRetainWalk keep) +{table_retain_truncate(keep.ids,probe->id_checkpoint); +} +static SCHEMA_UNUSED void table_retain_finish(TableWriter * w,TableRetainIds * ids) +{int32_t i=0,j=0; +TableRetain * r=ids->retain; +while(iknown.count||(r&&jid_used)){if(!r||j>=r->id_used||(iknown.count&&ids->slots[i]ids[j].slot))table_writer_put64(w,ids->known.ids[i++]); +else table_writer_put64(w,r->ids[j++].id); +}table_writer_put64(w,(uint64_t)ids->count); +} + +/* Capture writes only within the remaining record capacity. Failure leaves + used/count unchanged; a huge zero-bit message count reaches this bound + before it can command an unbounded transcode. */ +typedef struct TableRetainIn { + TableReader input;uint8_t * out;int64_t capacity,used;int failed; +} TableRetainIn; +static SCHEMA_UNUSED int table_retain_raw(TableRetainIn * s,const void * data,int64_t bytes) +{if(s->failed||bytes<0||s->used>s->capacity||bytes>s->capacity-s->used){s->failed=1; +return 0; +}if(bytes)memcpy(s->out+s->used,data,(size_t)bytes); +s->used+=bytes; +return 1; +} +static SCHEMA_UNUSED int table_retain_leb(TableRetainIn * s,uint64_t v) +{uint8_t b[10]; +int n=0; +do{b[n++]=(uint8_t)((v&127)|(v>=128?128:0)); +v>>=7; +}while(v); +return table_retain_raw(s,b,n); +} +static SCHEMA_UNUSED int table_retain_id(TableRetainIn * s,uint64_t id) +{uint8_t b[8]; +table_retain_put64(b,id); +return table_retain_raw(s,b,8); +} +static SCHEMA_UNUSED int table_retain_in_ref(TableRetainIn * s,int zero) +{uint64_t ref,id; +if(!table_reader_leb(&s->input,&ref))return 0; +if(!ref)return zero&&table_retain_id(s,0); +if(ref>s->input.id_count)return 0; +id=table_reader_id_at(&s->input,ref); +return id&&!table_retain_reserved(id)&&table_retain_id(s,id); +} +static SCHEMA_UNUSED int table_retain_in_payload(TableRetainIn *,uint8_t,int32_t); +static SCHEMA_UNUSED int table_retain_in_content(TableRetainIn *,uint8_t,int64_t,int32_t); +static SCHEMA_UNUSED int table_retain_in_frame(TableRetainIn * s,uint8_t kind,int64_t length,int32_t depth) +{uint8_t zero[8]={0}; +int64_t mark=s->used; +if(!table_retain_raw(s,zero,8)||!table_retain_in_content(s,kind,length,depth))return 0; +if(s->used-mark-8>UINT32_MAX)return 0; +table_retain_put32(s->out+mark,(uint32_t)(s->used-mark-8)); +return 1; +} +static SCHEMA_UNUSED int table_retain_in_content(TableRetainIn * s,uint8_t kind,int64_t length,int32_t depth) +{ + int64_t end; + TableReader * r=&s->input; + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX||!table_reader_has(r,length))return 0; + end=r->offset+length; + switch(kind){ + case 13:for(;;){uint64_t ref; + int64_t mark=r->offset; + uint8_t k; + if(!table_reader_leb(r,&ref))return 0; + if(!ref){if(!table_retain_id(s,0))return 0; + break; + }r->offset=mark; + if(!table_retain_in_ref(s,0)||r->offset>=end)return 0; + k=table_reader_get8(r); + if(!table_retain_raw(s,&k,1)||!table_retain_in_payload(s,k,depth)||r->offset>end)return 0; + }break; + case 14:case 16:{uint8_t elem; + uint64_t n,i; + if(r->offset>=end)return 0; + elem=table_reader_get8(r); + if(!table_retain_raw(s,&elem,1)||!table_reader_leb(r,&n)||!table_retain_leb(s,n))return 0; + for(i=0;i(uint64_t)(end-r->offset)||!table_retain_in_frame(s,elem,(int64_t)bytes,depth+1))return 0; + }else if(!table_retain_in_payload(s,elem,depth))return 0; + if(r->offset>end)return 0; + }break; + } + case 15:case 30:if(!table_retain_in_payload(s,kind,depth))return 0; + break; + case 17:return 0; + default:if(!table_retain_raw(s,r->buffer+r->offset,length))return 0; + r->offset+=length; + break; + }return r->offset==end; +} +static SCHEMA_UNUSED int table_retain_width(uint8_t kind) +{switch(kind){case 1:case 2:case 6:case 20:case 25:return 1; +case 3:case 7:case 21:case 26:return 2; +case 4:case 8:case 10:case 22:case 27:return 4; +case 5:case 9:case 11:case 23:case 28:return 8; +case 18:case 19:case 24:case 29:return 16; +default:return 0; +}} +static SCHEMA_UNUSED int table_retain_in_payload(TableRetainIn * s,uint8_t kind,int32_t depth) +{ + TableReader * r=&s->input; + uint64_t length; + int width=table_retain_width(kind); + if(width){if(!table_reader_has(r,width)||!table_retain_raw(s,r->buffer+r->offset,width))return 0; + r->offset+=width; + return 1; + } + switch(kind){ + case 12:case 31:case 32:case 33:if(!table_reader_leb(r,&length)||!table_reader_room(r,length)||!table_retain_leb(s,length)||!table_retain_raw(s,r->buffer+r->offset,(int64_t)length))return 0; + r->offset+=(int64_t)length; + return 1; + case 13:case 14:case 16:if(!table_reader_leb(r,&length)||!table_reader_room(r,length))return 0; + return table_retain_in_frame(s,kind,(int64_t)length,depth+1); + case 15:{uint64_t arm; + uint8_t k; + int64_t mark=r->offset; + if(!table_reader_leb(r,&arm))return 0; + r->offset=mark; + if(!table_retain_in_ref(s,1))return 0; + if(!arm)return 1; + if(!table_reader_has(r,1))return 0; + k=table_reader_get8(r); + if(!table_retain_raw(s,&k,1)||!table_reader_leb(r,&length)||!table_reader_room(r,length))return 0; + return table_retain_in_frame(s,k,(int64_t)length,depth+1); + } + case 30:return table_retain_in_ref(s,1); + default:return 0; + } +} +static SCHEMA_UNUSED uint8_t * table_retain_begin(TableRetainWalk keep,uint64_t id,uint8_t kind,TableRetainIn * s) +{ + TableRetain * r=keep.retain; + int64_t header=SCHEMA_TABLE_RETAIN_HEADER+8*(int64_t)keep.path.depth; + uint8_t * p; + int32_t i; + if(table_retain_reserved(id)||!r||!r->bytes||keep.path.depth<0||keep.path.depth>SCHEMA_TABLE_RETAIN_DEPTH_MAX||r->used<0||r->used>r->capacity||header>r->capacity-r->used||r->count==INT32_MAX)return NULL; + p=r->bytes+r->used; + table_retain_put32(p+4,keep.path.node); + table_retain_put32(p+8,(uint32_t)keep.path.depth); + table_retain_put64(p+16,id); + p[24]=kind; + p[25]=0; + for(i=0;iout=p+header; + s->capacity=r->capacity-r->used-header; + if(s->capacity>UINT32_MAX-header)s->capacity=UINT32_MAX-header; + return p; +} +static SCHEMA_UNUSED void table_retain_commit(TableRetainWalk keep,uint8_t * record,const TableRetainIn * s,TableReport * report) +{int64_t bytes=SCHEMA_TABLE_RETAIN_HEADER+8*(int64_t)keep.path.depth+s->used; +table_retain_put32(record,(uint32_t)bytes); +table_retain_put32(record+12,(uint32_t)s->used); +keep.retain->used+=bytes; +keep.retain->count++; +report->retained++; +} +static SCHEMA_UNUSED int table_retain_capture(TableReader * r,uint64_t id,uint8_t kind,TableRetainWalk keep) +{ + TableRetainIn s; + TableReader source=*r; + uint8_t * record; + if(!table_reader_skip(r,kind))return 0; + if(!keep.retain)return 1; + record=table_retain_begin(keep,id,kind,&s); + if(!record){r->report->retain_lost++; + return 1; + }source.size=r->offset; + s.input=source; + if(!table_retain_in_payload(&s,kind,0)||s.input.offset!=source.size){r->report->retain_lost++; + return 1; + }table_retain_commit(keep,record,&s,r->report); + return 1; +} + + +typedef struct TableRetainOut { + uint8_t * input;int64_t size,at,bytes;TableRetainIds * ids;TableWriter * writer;int overflow; +} TableRetainOut; +static SCHEMA_UNUSED int table_retain_out_raw(TableRetainOut * s,const void * data,int64_t bytes) +{if(bytes<0||bytes>INT64_MAX-s->bytes)return 0; +if(s->writer)table_writer_raw(s->writer,data,bytes); +s->bytes+=bytes; +return !s->writer||!s->writer->overflow; +} +static SCHEMA_UNUSED int table_retain_out_leb(TableRetainOut * s,uint64_t v) +{if(s->writer)table_writer_leb(s->writer,v); +s->bytes+=table_retain_leb_bytes(v); +return !s->writer||!s->writer->overflow; +} +static SCHEMA_UNUSED int table_retain_out_leb_read(TableRetainOut * s,uint64_t * v) +{TableReader r=table_reader_make(s->input,s->size,NULL); +r.offset=s->at; +if(!table_reader_leb(&r,v))return 0; +s->at=r.offset; +return 1; +} +static SCHEMA_UNUSED int table_retain_out_ref(TableRetainOut * s) +{uint64_t id,ref; +if(s->size-s->at<8)return 0; +id=table_retain_u64(s->input+s->at); +s->at+=8; +if(!id)return table_retain_out_leb(s,0); +ref=table_retain_ref(s->ids,id,1,&s->overflow); +return !s->ids->lost&&!s->overflow&&table_retain_out_leb(s,ref); +} +static SCHEMA_UNUSED int table_retain_out_payload(TableRetainOut *,uint8_t,int32_t); +static SCHEMA_UNUSED int table_retain_out_content(TableRetainOut *,uint8_t,int64_t,int32_t); +static SCHEMA_UNUSED int table_retain_out_frame(TableRetainOut * s,uint8_t kind,int32_t depth) +{ + uint8_t * slot; + int64_t resolved,wire,begin; + if(s->size-s->at<8)return 0; + slot=s->input+s->at; + resolved=table_retain_u32(slot); + s->at+=8; + if(!s->writer){begin=s->bytes; + if(!table_retain_out_content(s,kind,resolved,depth))return 0; + wire=s->bytes-begin; + if(wire>UINT32_MAX)return 0; + table_retain_put32(slot+4,(uint32_t)wire); + s->bytes+=table_retain_leb_bytes((uint64_t)wire); + return 1; + } + wire=table_retain_u32(slot+4); + if(!table_retain_out_leb(s,(uint64_t)wire))return 0; + begin=s->bytes; + return table_retain_out_content(s,kind,resolved,depth)&&s->bytes-begin==wire; +} +static SCHEMA_UNUSED int table_retain_out_content(TableRetainOut * s,uint8_t kind,int64_t length,int32_t depth) +{ + int64_t end; + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX||length<0||length>s->size-s->at)return 0; + end=s->at+length; + switch(kind){ + case 13:for(;;){uint64_t id; + uint8_t k; + if(end-s->at<8)return 0; + id=table_retain_u64(s->input+s->at); + if(!table_retain_out_ref(s))return 0; + if(!id)break; + if(s->at>=end)return 0; + k=s->input[s->at++]; + if(!table_retain_out_raw(s,&k,1)||!table_retain_out_payload(s,k,depth))return 0; + }break; + case 14:case 16:{uint64_t n,i; + uint8_t elem; + if(s->at>=end)return 0; + elem=s->input[s->at++]; + if(!table_retain_out_raw(s,&elem,1)||!table_retain_out_leb_read(s,&n)||!table_retain_out_leb(s,n))return 0; + for(i=0;iinput+s->at,length))return 0; + s->at+=length; + break; + }return s->at==end; +} +static SCHEMA_UNUSED int table_retain_out_payload(TableRetainOut * s,uint8_t kind,int32_t depth) +{ + int width=table_retain_width(kind); + uint64_t length; + if(width){if(width>s->size-s->at||!table_retain_out_raw(s,s->input+s->at,width))return 0; + s->at+=width; + return 1; + } + switch(kind){ + case 12:case 31:case 32:case 33:if(!table_retain_out_leb_read(s,&length)||length>(uint64_t)(s->size-s->at)||!table_retain_out_leb(s,length)||!table_retain_out_raw(s,s->input+s->at,(int64_t)length))return 0; + s->at+=(int64_t)length; + return 1; + case 13:case 14:case 16:return table_retain_out_frame(s,kind,depth+1); + case 15:{uint64_t arm; + uint8_t k; + if(s->size-s->at<8)return 0; + arm=table_retain_u64(s->input+s->at); + if(!table_retain_out_ref(s))return 0; + if(!arm)return 1; + if(s->at>=s->size)return 0; + k=s->input[s->at++]; + return table_retain_out_raw(s,&k,1)&&table_retain_out_frame(s,k,depth+1); + } + case 30:return table_retain_out_ref(s); + default:return 0; + } +} +static SCHEMA_UNUSED int64_t table_retain_record_measure(uint8_t * record,TableRetainIds * ids,uint64_t * ref) +{ + int32_t mark=ids->count; + TableRetainOut s; + int overflow=0; + ids->lost=0; + *ref=table_retain_ref(ids,table_retain_u64(record+16),1,&overflow); + if(!ids->lost&&!overflow){memset(&s,0,sizeof(s)); + s.input=table_retain_payload(record); + s.size=table_retain_u32(record+12); + s.ids=ids; + if(table_retain_out_payload(&s,record[24],0)&&s.at==s.size)return table_retain_leb_bytes(*ref)+1+s.bytes; + } + table_retain_truncate(ids,mark); + ids->lost=0; + return -1; +} +static SCHEMA_UNUSED int table_retain_tail(TableWriter * w,TableRetainWalk keep) +{ + TableRetain * r=keep.retain; + int32_t i; + int64_t at=0; + if(!r||!r->bytes)return 1; + for(i=0;icount;i++){uint8_t * record=r->bytes+at; + uint64_t ref; + int64_t bytes; + TableRetainOut s; + at+=table_retain_u32(record); + if(!table_retain_here(r,record,&keep.path))continue; + if(w->check_default){w->offset=2; + return 1; + } + bytes=table_retain_record_measure(record,keep.ids,&ref); + if(bytes<0)continue; + if(!w->buffer){if(bytes>INT64_MAX-w->offset){w->overflow=1; + return 0; + }w->offset+=bytes; + continue; + } + table_writer_leb(w,ref); + table_writer_put8(w,record[24]); + memset(&s,0,sizeof(s)); + s.input=table_retain_payload(record); + s.size=table_retain_u32(record+12); + s.ids=keep.ids; + s.writer=w; + if(!table_retain_out_payload(&s,record[24],0)||s.at!=s.size)return 0; + record[25]=1; + }return !w->overflow; +} +static SCHEMA_UNUSED void table_retain_clear_placed(TableRetain * r) +{int32_t i; +int64_t at=0; +if(!r||!r->bytes)return; +for(i=0;icount;i++){r->bytes[at+25]=0; +at+=table_retain_u32(r->bytes+at); +}} +static SCHEMA_UNUSED void table_retain_count_lost(TableRetain * r,TableReport * report) +{int32_t i; +int64_t at=0; +if(!r||!r->bytes)return; +for(i=0;icount;i++){if(!r->bytes[at+25])report->retain_lost++; +at+=table_retain_u32(r->bytes+at); +}} + + +typedef struct TableRetainMessage {TableRetainIn output;TableMessageReader input;} TableRetainMessage; +static SCHEMA_UNUSED int table_retain_message_payload(TableRetainMessage *,const TableMessageEntry *,int32_t); +static SCHEMA_UNUSED int table_retain_message_content(TableRetainMessage *,const TableMessageEntry *,int32_t); +static SCHEMA_UNUSED int table_retain_message_frame(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{uint8_t zero[8]={0}; +int64_t mark=s->output.used; +if(!table_retain_raw(&s->output,zero,8)||!table_retain_message_content(s,e,depth))return 0; +if(s->output.used-mark-8>UINT32_MAX)return 0; +table_retain_put32(s->output.out+mark,(uint32_t)(s->output.used-mark-8)); +return 1; +} +static SCHEMA_UNUSED int table_retain_message_scalar(TableRetainMessage * s,const TableMessageEntry * e) +{ + uint64_t lo=0,hi=0; + uint8_t bytes[16]; + int width=table_retain_width(e->kind); + if(!width)return 0; + if(e->kind==10&&e->packing==2){float value; + if(!table_message_float(&s->input,e,&value))return 0; + lo=table_float_to_bits(value); + } + else if(!table_message_number(&s->input,e,&lo,&hi))return 0; + table_retain_put64(bytes,lo); + table_retain_put64(bytes+8,hi); + return table_retain_raw(&s->output,bytes,width); +} +static SCHEMA_UNUSED int table_retain_message_opaque(TableRetainMessage * s,const TableMessageEntry * e,int framed) +{ + uint64_t n,i; + TableMessageReader * r=&s->input; + if(e->kind==12||e->kind==33){if(!table_bit_get(&r->bits,&n,table_bits_required(0,e->max)))return 0; + if(e->kind==33)n*=2; + else if(!table_bit_align_read(&r->bits))return 0; + } + else if(!table_bit_align_read(&r->bits)||!table_bit_get(&r->bits,&n,32))return 0; + if(n>INT64_MAX/8||!table_bit_has(&r->bits,(int64_t)n*8))return 0; + if(framed&&!table_retain_leb(&s->output,n))return 0; + if(n>(uint64_t)(s->output.capacity-s->output.used))return 0; + for(i=0;ibits,&v,8))return 0; + byte=(uint8_t)v; + if(!table_retain_raw(&s->output,&byte,1))return 0; + }return 1; +} +static SCHEMA_UNUSED int table_retain_message_content(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{ + if(depth>SCHEMA_TABLE_RETAIN_WALK_MAX)return 0; + switch(e->kind){ + case 13:for(;;){const TableMessageEntry * f; + if(!table_message_ref(&s->input,&f,0))return 0; + if(!f)return table_retain_id(&s->output,0); + if(!f->id||!table_retain_id(&s->output,f->id)||!table_retain_raw(&s->output,&f->kind,1)||!table_retain_message_payload(s,f,depth))return 0; + } + case 14:case 16:{uint64_t n,i; + TableMessageEntry elem=table_message_element(e); + if(elem.kind==17||!table_message_count(&s->input,e,&n)||!table_retain_raw(&s->output,&elem.kind,1)||!table_retain_leb(&s->output,n))return 0; + for(i=0;ikind==16){const TableMessageEntry * key; + if(!table_message_ref(&s->input,&key,1)||!key||!key->id||!table_retain_id(&s->output,key->id)||!table_retain_message_frame(s,&elem,depth+1))return 0; + }else if(!table_retain_message_payload(s,&elem,depth))return 0; + }return 1; + } + case 15:case 30:return table_retain_message_payload(s,e,depth); + case 32:return 1; + case 12:case 31:case 33:return table_retain_message_opaque(s,e,0); + case 0:case 17:return 0; + default:return table_retain_message_scalar(s,e); + } +} +static SCHEMA_UNUSED int table_retain_message_payload(TableRetainMessage * s,const TableMessageEntry * e,int32_t depth) +{ + switch(e->kind){ + case 0:case 17:return 0; + case 32:return table_retain_leb(&s->output,0); + case 30:{const TableMessageEntry * entry; + if(!table_message_ref(&s->input,&entry,1))return 0; + return table_retain_id(&s->output,entry?entry->id:0); + } + case 15:{const TableMessageEntry * arm; + if(!table_message_ref(&s->input,&arm,0))return 0; + if(!arm)return table_retain_id(&s->output,0); + return arm->id&&arm->kind&&table_retain_id(&s->output,arm->id)&&table_retain_raw(&s->output,&arm->kind,1)&&table_retain_message_frame(s,arm,depth+1); + } + case 13:case 14:case 16:return table_retain_message_frame(s,e,depth+1); + case 12:case 31:case 33:return table_retain_message_opaque(s,e,1); + default:return table_retain_message_scalar(s,e); + } +} +static SCHEMA_UNUSED int table_retain_message_capture(TableMessageReader * r,const TableMessageEntry * entry,TableRetainWalk keep) +{ + TableRetainMessage s; + uint8_t * record; + s.input=*r; + if(!table_message_skip(r,entry))return 0; + if(!keep.retain)return 1; + record=table_retain_begin(keep,entry->id,entry->kind,&s.output); + if(!record||!table_retain_message_payload(&s,entry,0)||s.input.bits.offset!=r->bits.offset){r->report->retain_lost++; + return 1; + } + table_retain_commit(keep,record,&s.output,r->report); + return 1; +} + +typedef int (*TableRetainSaveNode)(TableWriter *,const void *,uint64_t,TableRetainWalk); +static SCHEMA_UNUSED int table_retain_records(TableWriter * w,TableRetainWalk retention,TableRetainSaveNode save) +{ + uint64_t i; + const TableNumbering * n=w->nodes; + table_writer_leb(w,n->count-1); + for(i=1;icount;i++){const TableNodeEntry * e=n->entries+i; + retention.path=table_retain_root(e->value,0); + table_retain_writer_id(w,e->type->id,retention); + if(!w->buffer){int64_t begin=w->offset; + if(!save(w,e->value,e->type->id,retention))return 0; + table_writer_leb(w,(uint64_t)(w->offset-begin)); + } + else {TableWriter probe=table_retain_probe(w,retention); + if(!save(&probe,e->value,e->type->id,retention))return 0; + table_retain_rewind(&probe,retention); + table_writer_leb(w,(uint64_t)probe.offset); + if(!save(w,e->value,e->type->id,retention))return 0; + }} + return !w->overflow; +} +static SCHEMA_UNUSED int64_t table_retain_graph_save(const void * root,const TableNodeType * type,TableRetain * retain,uint8_t * buffer,int64_t capacity,TableReport * report,TableAllocator allocator,TableRetainSaveNode save) +{ + TableNumbering numbering; + TableRetainIds ids; + TableRetainWalk retention; + TableWriter w; + int64_t result=-1; + memset(&ids,0,sizeof(ids)); + ids.retain=retain; + memset(&retention,0,sizeof(retention)); + retention.retain=retain; + retention.ids=&ids; + retention.path=table_retain_root(root,1); + if(retain)retain->id_used=0; + if(buffer)table_retain_clear_placed(retain); + w=table_writer_make(buffer,capacity,&ids.known); + if(!table_number_build(&numbering,NULL,root,type,allocator)){table_number_dispose(&numbering); + return -1; + }w.nodes=&numbering; + table_writer_put8(&w,1); + if(!save(&w,root,type->id,retention))goto done; + w.offset--; + if(numbering.count>1){table_retain_writer_id(&w,UINT64_MAX,retention); + table_writer_put8(&w,12); + if(!buffer){int64_t begin=w.offset; + if(!table_retain_records(&w,retention,save))goto done; + table_writer_leb(&w,(uint64_t)(w.offset-begin)); + } + else {TableWriter probe=table_retain_probe(&w,retention); + if(!table_retain_records(&probe,retention,save))goto done; + table_retain_rewind(&probe,retention); + table_writer_leb(&w,(uint64_t)probe.offset); + if(!table_retain_records(&w,retention,save))goto done; + }} + table_writer_put8(&w,0); + table_retain_finish(&w,&ids); + if(!w.overflow){result=w.offset; + if(buffer)table_retain_count_lost(retain,report); + } + done:table_number_dispose(&numbering); + return result; +} + +#endif + +/* table Stamp — TABLE-wire storage: relocatable, bounded. C has no member + initializers, so the declared defaults live in stamp_reset and nowhere + else — one definition of what a default is (docs/SPEC-TABLES.md) */ +typedef struct Stamp { + char tag[8 + 1]; /* string(8): N + 1 for the terminator the wire does not carry */ + int32_t tag_length; + int32_t seq; +} Stamp; + +/* ---- prefill: the declared defaults, in place (docs/SPEC-TABLES.md) ---- */ + +static SCHEMA_UNUSED void stamp_reset( Stamp * value ); +static SCHEMA_UNUSED void schema_graphdemo_stamp_reset_raw_( void * storage ); +static SCHEMA_UNUSED void colour_reset( Colour * value ); +static SCHEMA_UNUSED void schema_graphdemo_colour_reset_raw_( void * storage ); + +static SCHEMA_UNUSED void stamp_reset( Stamp * value ) +{ + memset( value->tag, 0, sizeof( value->tag ) ); + value->tag_length = 0; + value->seq = 0; +} + +static SCHEMA_UNUSED void schema_graphdemo_stamp_reset_raw_( void * storage ) { stamp_reset( (Stamp *) storage ); } + static SCHEMA_UNUSED void colour_reset( Colour * value ) { value->r = 0; @@ -943,55 +2985,90 @@ static SCHEMA_UNUSED int64_t colour_measure( const Colour * value ); static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int colour_save_body( TableWriter * w, const Colour * value ); static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int colour_load_body( TableReader * r, Colour * value ); -static SCHEMA_UNUSED int64_t stamp_measure( const Stamp * value ) +static SCHEMA_UNUSED int schema_graphdemo_stamp_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int schema_graphdemo_colour_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order); +static SCHEMA_UNUSED int stamp_save_message_body(TableBitWriter *,const Stamp *); +static SCHEMA_UNUSED int colour_save_message_body(TableBitWriter *,const Colour *); +static SCHEMA_UNUSED int stamp_load_message_body(TableMessageReader *,Stamp *); +static SCHEMA_UNUSED int colour_load_message_body(TableMessageReader *,Colour *); +static SCHEMA_UNUSED int schema_graphdemo_stamp_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_colour_message_extent_(TableMessageReader *,int64_t *); +static SCHEMA_UNUSED int schema_graphdemo_stamp_wire_tag_( TableWriter * w, const Stamp * value ) { - int64_t bytes = 2; /* terminator */ - if ( value->tag_length < 0 || value->tag_length > 8 ) { return -1; } /* storage invariant */ - if ( value->tag_length > 0 ) { bytes += 3 + 4 + value->tag_length; } /* tag */ - if ( value->seq != 0 ) { bytes += 3 + 4; } /* seq */ - return bytes; + if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; } + table_writer_raw( w, value->tag, value->tag_length ); + return !w->overflow; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int stamp_save_body( TableWriter * w, const Stamp * value ) +static SCHEMA_UNUSED int stamp_save_body( TableWriter * w, const Stamp * value ) { - if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; } /* storage invariant */ - if ( value->tag_length > 0 ) - { - table_writer_put16( w, 0xbc64 ); table_writer_put8( w, 12 ); /* tag */ - table_writer_put32( w, (uint32_t) value->tag_length ); - table_writer_raw( w, value->tag, value->tag_length ); + (void) value; + { /* tag */ + if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; } + if ( value->tag_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x56d7ab194448a4f3ull ); table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_stamp_wire_tag_( w, value ) ) { return 0; } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_writer_probe( w ); + if ( !schema_graphdemo_stamp_wire_tag_( &probe, value ) ) { return 0; } + table_writer_rewind(&probe); table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_stamp_wire_tag_( w, value ) ) { return 0; } + } + } } - if ( value->seq != 0 ) - { - table_writer_put16( w, 0xc29b ); table_writer_put8( w, 4 ); /* seq */ - table_writer_put32( w, (uint32_t) ( value->seq ) ); + { /* seq */ + if ( !( value->seq == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0x823b8a195ce2133cull ); table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->seq ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t stamp_measure( const Stamp * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !stamp_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t stamp_save( const Stamp * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !stamp_save_body( &w, value ) ) { return -1; } - return w.offset; /* == stamp_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int stamp_load_body( TableReader * r, Stamp * value ) +static SCHEMA_UNUSED int stamp_load_body( TableReader * r, Stamp * value ) { - stamp_reset( value ); /* prefill declared defaults in place, then overlay */ + stamp_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xbc64: /* tag */ + case 0x56d7ab194448a4f3ull: /* tag */ { if ( kind != 12 ) { @@ -999,107 +3076,331 @@ static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int stamp_load_body( TableRea if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - uint32_t len; - uint32_t keep; - if ( !table_reader_has( r, 4 ) ) { r->report->malformed = 1; return 0; } - len = table_reader_get32( r ); - if ( !table_reader_has( r, len ) ) { r->report->malformed = 1; return 0; } - keep = len; - if ( keep > 8 ) { keep = 8; r->report->clamped++; } - memcpy( value->tag, r->buffer + r->offset, keep ); - value->tag[keep] = 0; - value->tag_length = (int32_t) keep; - r->offset += len; + TableReader sub; uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; return 0; } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; value->tag[0] = 0; value->tag_length = 0; break; + } + keep = (uint64_t) sub.size; + if ( keep > 8 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 8 ); r->report->clamped++; } + memcpy( value->tag, sub.buffer, (size_t) keep ); value->tag[keep] = 0; value->tag_length = (int32_t) keep; break; } - case 0xc29b: /* seq */ + case 0x823b8a195ce2133cull: /* seq */ { if ( kind != 4 ) { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->seq = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->seq = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->seq = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; + } + r->report->widened++; + break; + } r->report->kind_mismatch++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); - if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } - else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } - value->seq = decoded_v; + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->seq = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; return 0; } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; r->report->clamped++; } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; r->report->clamped++; } + value->seq = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; return 0; } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; r->report->clamped++; } + else if ( decoded_v > 1000 ) { decoded_v = 1000; r->report->clamped++; } + value->seq = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int stamp_load( Stamp * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + stamp_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return stamp_load_body( &r, value ); } -static SCHEMA_UNUSED int64_t colour_measure( const Colour * value ) +static SCHEMA_UNUSED int stamp_save_message_body(TableBitWriter * w,const Stamp * value) { - int64_t bytes = 2; /* terminator */ - if ( value->r != 0 ) { bytes += 3 + 1; } /* r */ - if ( value->g != 0 ) { bytes += 3 + 1; } /* g */ - if ( value->b != 0 ) { bytes += 3 + 1; } /* b */ - return bytes; + (void)value; + { /* tag */ + if(value->tag_length<0 || value->tag_length>8) return 0; + if(value->tag_length != 0) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,19,kTableMessageRefBitsHere); + if(value->tag_length<0 || value->tag_length>8) return 0; + table_bit_put(w,(uint64_t)value->tag_length,4); + table_bit_align_write(w); + table_bit_putbytes(w,value->tag,value->tag_length); + } + } + { /* seq */ + if(!(value->seq == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,30,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->seq)-UINT64_C(0),10); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; } - -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int colour_save_body( TableWriter * w, const Colour * value ) +static SCHEMA_UNUSED int64_t stamp_measure_messages(const Stamp * values,int64_t count,TableReport * report) { - if ( value->r != 0 ) - { - table_writer_put16( w, 0xb019 ); table_writer_put8( w, 6 ); /* r */ - table_writer_put8( w, (uint8_t) ( value->r ) ); + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0x56d7ab194448a4f3): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,8); + memcpy(value->tag,text,(size_t)kept); + value->tag[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->tag_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x823b8a195ce2133c): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>1000ll){decoded=1000ll; + r->report->clamped++; + } + value->seq=(int32_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int stamp_load_messages(Stamp * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int colour_save_body( TableWriter * w, const Colour * value ) +{ + (void) value; + { /* r */ + if ( !( value->r == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63ef4c86020cd5ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->r ) ); + } } - if ( value->g != 0 ) - { - table_writer_put16( w, 0xc40a ); table_writer_put8( w, 6 ); /* g */ - table_writer_put8( w, (uint8_t) ( value->g ) ); + { /* g */ + if ( !( value->g == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63da4c8601e926ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->g ) ); + } } - if ( value->b != 0 ) - { - table_writer_put16( w, 0xcae9 ); table_writer_put8( w, 6 ); /* b */ - table_writer_put8( w, (uint8_t) ( value->b ) ); + { /* b */ + if ( !( value->b == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; return 1; } + table_writer_id( w, 0xaf63df4c8601f1a5ull ); table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->b ) ); + } } - table_writer_put16( w, 0 ); /* terminator */ + table_writer_leb( w, 0 ); return !w->overflow; } +static SCHEMA_UNUSED int64_t colour_measure( const Colour * value ) +{ + TableWriteIds vocabulary; TableWriter w = table_writer_make( NULL, INT64_MAX, &vocabulary ); + table_writer_put8( &w, 1 ); + if ( !colour_save_body( &w, value ) ) { return -1; } + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; +} + static SCHEMA_UNUSED int64_t colour_save( const Colour * value, uint8_t * buffer, int64_t capacity ) { - TableWriter w = table_writer_make( buffer, capacity ); + if ( buffer == NULL || capacity < 0 ) { return -1; } + TableWriteIds vocabulary; TableWriter w = table_writer_make( buffer, capacity, &vocabulary ); + table_writer_put8( &w, 1 ); if ( !colour_save_body( &w, value ) ) { return -1; } - return w.offset; /* == colour_measure( value ) */ + table_writer_finish( &w ); + return w.overflow ? -1 : w.offset; } -static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int colour_load_body( TableReader * r, Colour * value ) +static SCHEMA_UNUSED int colour_load_body( TableReader * r, Colour * value ) { - colour_reset( value ); /* prefill declared defaults in place, then overlay */ + colour_reset( value ); for ( ;; ) { - uint16_t field_id; - uint8_t kind; - if ( !table_reader_has( r, 2 ) ) { r->report->malformed = 1; return 0; } - field_id = table_reader_get16( r ); - if ( field_id == 0 ) { return 1; } - if ( !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } - kind = table_reader_get8( r ); - switch ( field_id ) + uint64_t ref, id; uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; return 0; } + if ( ref == 0 ) { return 1; } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; return 0; } + id = table_reader_id_at( r, ref ); kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; return 0; } + switch ( id ) { - case 0xb019: /* r */ + case 0xaf63ef4c86020cd5ull: /* r */ { if ( kind != 6 ) { @@ -1107,14 +3408,22 @@ static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int colour_load_body( TableRe if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->r = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->r = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xc40a: /* g */ + case 0xaf63da4c8601e926ull: /* g */ { if ( kind != 6 ) { @@ -1122,14 +3431,22 @@ static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int colour_load_body( TableRe if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->g = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->g = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } - case 0xcae9: /* b */ + case 0xaf63df4c8601f1a5ull: /* b */ { if ( kind != 6 ) { @@ -1137,32 +3454,803 @@ static SCHEMA_UNUSED SCHEMA_GRAPHDEMO_TABLE_INLINE int colour_load_body( TableRe if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; } - if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + switch ( kind ) { - uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); - value->b = decoded_v; + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; return 0; } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->b = decoded_v; + } + break; + } + default: r->report->malformed = 1; return 0; } break; } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; return 0; } + break; default: - { r->report->unknown++; if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; return 0; } break; - } } } } static SCHEMA_UNUSED int colour_load( Colour * value, const uint8_t * buffer, int64_t bytes, TableReport * report ) { - TableReport ignored; - TableReader r; + TableReport ignored; TableReader r; memset( &ignored, 0, sizeof( ignored ) ); - r = table_reader_make( buffer, bytes, report != NULL ? report : &ignored ); + if ( report == NULL ) { report = &ignored; } + colour_reset( value ); + if ( !table_wire_open( &r, buffer, bytes, report ) ) { return 0; } return colour_load_body( &r, value ); } +static SCHEMA_UNUSED int colour_save_message_body(TableBitWriter * w,const Colour * value) +{ + (void)value; + { /* r */ + if(!(value->r == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,7,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->r)-UINT64_C(0),8); + } + } + { /* g */ + if(!(value->g == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,8,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->g)-UINT64_C(0),8); + } + } + { /* b */ + if(!(value->b == 0)) { + if(w->check_default) {w->bits=kTableMessageRefBitsHere+1; + return 1; + } + table_bit_put(w,9,kTableMessageRefBitsHere); + table_bit_put(w,(uint64_t)(value->b)-UINT64_C(0),8); + } + } + table_bit_put(w,0,kTableMessageRefBitsHere); + return !w->overflow; +} +static SCHEMA_UNUSED int64_t colour_measure_messages(const Colour * values,int64_t count,TableReport * report) +{ + TableBitWriter w=table_bit_writer(NULL,INT64_MAX); + int64_t i; + if(count<1 || values==NULL) return -1; + if(count>kTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;ikTableMessageBatchMax) {if(report!=NULL) {report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + }return -1; + } + if(buffer==NULL || capacity<0) return -1; + table_bit_put(&w,2,8); + table_bit_put(&w,(uint64_t)(count-1),8); + for(i=0;iid){ + case UINT64_C(0xaf63ef4c86020cd5): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->r=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xaf63da4c8601e926): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->g=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xaf63df4c8601f1a5): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->b=(uint8_t)decoded; + } + break; + } + default:r->report->unknown++; + if(!table_message_skip(r,entry))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int colour_load_messages(Colour * values,int64_t * count,const TableVocabulary * vocabulary,const uint8_t * buffer,int64_t bytes,TableReport * report) +{ + TableReport ignored={0}; + TableMessageReader r; + int64_t capacity,bodies,i; + if(report==NULL)report=&ignored; + if(values==NULL||count==NULL){report->malformed=1; + return 0; + }capacity=*count; + *count=0; + bodies=table_message_open(&r,vocabulary,buffer,bytes,report); + if(bodies<0)return 0; + if(bodies>capacity){*count=bodies; + report->refused=1; + report->reason=SCHEMA_TABLE_BATCH_TOO_LARGE; + return 0; + } + for(i=0;iid){ + default:break; + }if(!table_message_skip(r,entry))return 0; + } +} +static SCHEMA_UNUSED int stamp_save_body_retain(TableWriter *,const Stamp *, TableRetainWalk retention); +static SCHEMA_UNUSED int stamp_load_body_retain(TableReader *,Stamp *, TableRetainWalk retention); +static SCHEMA_UNUSED int colour_save_body_retain(TableWriter *,const Colour *, TableRetainWalk retention); +static SCHEMA_UNUSED int colour_load_body_retain(TableReader *,Colour *, TableRetainWalk retention); +static SCHEMA_UNUSED int stamp_load_message_body_retain(TableMessageReader *,Stamp *, TableRetainWalk retention); +static SCHEMA_UNUSED int colour_load_message_body_retain(TableMessageReader *,Colour *, TableRetainWalk retention); +static SCHEMA_UNUSED int schema_graphdemo_stamp_wire_tag__retain( TableWriter * w, const Stamp * value , TableRetainWalk retention) +{ + (void)retention; + + if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; + } + table_writer_raw( w, value->tag, value->tag_length ); + return !w->overflow; +} + +static SCHEMA_UNUSED int stamp_save_body_retain( TableWriter * w, const Stamp * value , TableRetainWalk retention) +{ + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* tag */ + retention=table_retain_step(body_keep,0,0); + if ( value->tag_length < 0 || value->tag_length > 8 ) { return 0; + } + if ( value->tag_length != 0 ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x56d7ab194448a4f3ull , retention); + table_writer_put8( w, 12 ); + if ( w->buffer == NULL ) + { + int64_t frame_begin = w->offset; + if ( !schema_graphdemo_stamp_wire_tag__retain( w, value , retention) ) { return 0; + } + table_writer_leb( w, (uint64_t)(w->offset - frame_begin) ); + } + else + { + TableWriter probe = table_retain_probe( w , retention); + if ( !schema_graphdemo_stamp_wire_tag__retain( &probe, value , retention) ) { return 0; + } + table_retain_rewind(&probe, retention); + table_writer_leb( w, (uint64_t) probe.offset ); + if ( !schema_graphdemo_stamp_wire_tag__retain( w, value , retention) ) { return 0; + } + } + } + } + { /* seq */ + retention=table_retain_step(body_keep,1,0); + if ( !( value->seq == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0x823b8a195ce2133cull , retention); + table_writer_put8( w, 4 ); + table_writer_put32( w, (uint32_t) ( value->seq ) ); + } + } + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int stamp_load_body_retain( TableReader * r, Stamp * value , TableRetainWalk retention) +{ + (void)retention; + + stamp_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + for ( ;; ) + { + uint64_t ref, id; + uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); + kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) + { + case 0x56d7ab194448a4f3ull: /* tag */ + { + if ( kind != 12 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + TableReader sub; + uint64_t keep; + if ( !table_reader_span( r, &sub ) ) { r->report->malformed = 1; + return 0; + } + if ( !table_wire_utf8( sub.buffer, (uint64_t) sub.size ) ) + { + r->report->malformed = 1; + value->tag[0] = 0; + value->tag_length = 0; + break; + } + keep = (uint64_t) sub.size; + if ( keep > 8 ) { keep = table_wire_utf8_clamp( sub.buffer, keep, 8 ); + r->report->clamped++; + } + memcpy( value->tag, sub.buffer, (size_t) keep ); + value->tag[keep] = 0; + value->tag_length = (int32_t) keep; + break; + } + case 0x823b8a195ce2133cull: /* seq */ + { + if ( kind != 4 ) + { + if ( table_kind_widens( kind, 4 ) ) + { + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; + r->report->clamped++; + } + value->seq = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; + r->report->clamped++; + } + value->seq = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 1000 ) { decoded_v = 1000; + r->report->clamped++; + } + value->seq = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + r->report->widened++; + break; + } + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + switch ( kind ) + { + case 2: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int8_t) table_reader_get8( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; + r->report->clamped++; + } + value->seq = decoded_wide; + break; + } + case 3: + { + if ( !table_reader_has( &(*r), 2 ) ) { r->report->malformed = 1; + return 0; + } + int64_t decoded_wide = (int16_t) table_reader_get16( &(*r) ); + if ( decoded_wide < 0ll ) { decoded_wide = 0ll; + r->report->clamped++; + } + if ( decoded_wide > 1000ll ) { decoded_wide = 1000ll; + r->report->clamped++; + } + value->seq = decoded_wide; + break; + } + case 4: + { + if ( !table_reader_has( &(*r), 4 ) ) { r->report->malformed = 1; + return 0; + } + { + int32_t decoded_v = (int32_t) table_reader_get32( &(*r) ); + if ( decoded_v < 0 ) { decoded_v = 0; + r->report->clamped++; + } + else if ( decoded_v > 1000 ) { decoded_v = 1000; + r->report->clamped++; + } + value->seq = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + break; + } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } + break; + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; + } + } +} + +static SCHEMA_UNUSED int stamp_load_message_body_retain(TableMessageReader * r,Stamp * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + stamp_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0x56d7ab194448a4f3): { + if(!(entry->kind==12 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,12)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + {uint64_t n,kept; + if(!table_bit_get(&r->bits,&n,table_bits_required(0,entry->max)))goto malformed; + if(!table_bit_align_read(&r->bits))goto malformed; + if(n>INT64_MAX/8 || !table_bit_has(&r->bits,(int64_t)n*8))goto malformed; + {const uint8_t * text=r->bits.buffer+(r->bits.offset>>3); + if(!table_wire_utf8(text,n))goto malformed; + kept=table_wire_utf8_clamp(text,n,8); + memcpy(value->tag,text,(size_t)kept); + value->tag[kept]=0; + } + r->bits.offset+=(int64_t)n*8; + if(keptreport->clamped++; + value->tag_length=(int32_t)kept; + } + break; + } + case UINT64_C(0x823b8a195ce2133c): { + if(!(entry->kind==4 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,4)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + int64_t decoded=(int64_t)lo; + (void)hi; + if(decoded<0ll){decoded=0ll; + r->report->clamped++; + } + if(decoded>1000ll){decoded=1000ll; + r->report->clamped++; + } + value->seq=(int32_t)decoded; + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +static SCHEMA_UNUSED int colour_save_body_retain( TableWriter * w, const Colour * value , TableRetainWalk retention) +{ + (void)retention; + + (void) value; + TableRetainWalk body_keep=retention; + { /* r */ + retention=table_retain_step(body_keep,0,0); + if ( !( value->r == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xaf63ef4c86020cd5ull , retention); + table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->r ) ); + } + } + { /* g */ + retention=table_retain_step(body_keep,1,0); + if ( !( value->g == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xaf63da4c8601e926ull , retention); + table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->g ) ); + } + } + { /* b */ + retention=table_retain_step(body_keep,2,0); + if ( !( value->b == 0 ) ) + { + if ( w->check_default ) { w->offset = 2; + return 1; + } + table_retain_writer_id( w, 0xaf63df4c8601f1a5ull , retention); + table_writer_put8( w, 6 ); + table_writer_put8( w, (uint8_t) ( value->b ) ); + } + } + retention=body_keep; + if(!table_retain_tail(w,retention))return 0; + table_writer_leb( w, 0 ); + return !w->overflow; +} + +static SCHEMA_UNUSED int colour_load_body_retain( TableReader * r, Colour * value , TableRetainWalk retention) +{ + (void)retention; + + colour_reset( value ); + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + for ( ;; ) + { + uint64_t ref, id; + uint8_t kind; + if ( !table_reader_leb( r, &ref ) ) { r->report->malformed = 1; + return 0; + } + if ( ref == 0 ) { return 1; + } + if ( ref > r->id_count || !table_reader_has( r, 1 ) ) { r->report->malformed = 1; + return 0; + } + id = table_reader_id_at( r, ref ); + kind = table_reader_get8( r ); + if ( (id == 0xffffffffffffffffull && r->nested) || id == 0xfffffffffffffffeull || id == 0xfffffffffffffffdull ) { r->report->malformed = 1; + return 0; + } + switch ( id ) + { + case 0xaf63ef4c86020cd5ull: /* r */ + { + if ( kind != 6 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,0,0); + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->r = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + break; + } + case 0xaf63da4c8601e926ull: /* g */ + { + if ( kind != 6 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,1,0); + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->g = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + break; + } + case 0xaf63df4c8601f1a5ull: /* b */ + { + if ( kind != 6 ) + { + r->report->kind_mismatch++; + if ( !table_reader_skip( r, kind ) ) { r->report->malformed = 1; + return 0; + } + break; + } + retention=table_retain_step(body_keep,2,0); + switch ( kind ) + { + case 6: + { + if ( !table_reader_has( &(*r), 1 ) ) { r->report->malformed = 1; + return 0; + } + { + uint8_t decoded_v = (uint8_t) table_reader_get8( &(*r) ); + value->b = decoded_v; + } + break; + } + default: r->report->malformed = 1; + return 0; + } + break; + } + case UINT64_MAX: + if(r->nodes==NULL) { r->report->unknown++; + r->report->retain_lost += retention.retain != NULL; + } + if(!table_reader_skip(r,kind)) { r->report->malformed=1; + return 0; + } + break; + default: + r->report->unknown += 1; + if(!table_retain_capture(r,id,kind,body_keep)){r->report->malformed=1; + return 0; + }break; + } + } +} + +static SCHEMA_UNUSED int colour_load_message_body_retain(TableMessageReader * r,Colour * value, TableRetainWalk retention) +{ + (void)retention; + + TableRetainWalk body_keep=retention; + table_retain_discard(retention,0,0); + colour_reset(value); + for(;;){const TableMessageEntry * entry; + if(!table_message_ref(r,&entry,0))goto malformed; + if(entry==NULL)return 1; + switch(entry->id){ + case UINT64_C(0xaf63ef4c86020cd5): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,0,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->r=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xaf63da4c8601e926): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,1,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->g=(uint8_t)decoded; + } + break; + } + case UINT64_C(0xaf63df4c8601f1a5): { + if(!(entry->kind==6 && entry->elem_kind==0)){if(entry->elem_kind==0 && table_kind_widens(entry->kind,6)){r->report->widened++; + }else{r->report->kind_mismatch++; + if(!table_message_skip(r,entry))goto malformed; + break; + }} + retention=table_retain_step(body_keep,2,0); + { uint64_t lo,hi; + if(!table_message_number(r,entry,&lo,&hi))goto malformed; + uint64_t decoded=(uint64_t)lo; + (void)hi; + if(decoded>255ull){decoded=255ull; + r->report->clamped++; + } + value->b=(uint8_t)decoded; + } + break; + } + default:r->report->unknown += 1; + if(!table_retain_message_capture(r,entry,body_keep))goto malformed; + break; + } } + malformed:r->report->malformed=1; + return 0; +} +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define stamp_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define stamp_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define stamp_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define stamp_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define stamp_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define colour_load_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define colour_measure_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define colour_save_retain(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retention requires a variable root loaded into a region (SPEC-TABLES section 6.6). */ +#define colour_load_retain_messages(...) ((void)sizeof(struct { int retention_requires_variable_region_root : -1; })) +/* Retained fields have no announced slots: save the FILE form or relay the original message. */ +#define colour_save_retain_messages(...) ((void)sizeof(struct { int retention_message_write_requires_file_form : -1; })) /* ---- the cooked form: point at a cook (docs/SPEC-TABLES.md §7) ---- */ /* stamp_open: match the header and POINT. On a match the bytes ARE what this @@ -1184,11 +4272,41 @@ static SCHEMA_UNUSED int colour_load( Colour * value, const uint8_t * buffer, in file whose provenance a person doubts is schema cook-check, offline, over the ATTRIBUTION part beside the data — a person's decision, never a parameter on a load. */ +static SCHEMA_UNUSED const Stamp * stamp_open_ex(const void * bytes,uint64_t length,TableRefuseReason * reason) +{ return (const Stamp *)table_cook_open_ex(bytes,length,sizeof(Stamp),SCHEMA_TABLE_ALIGNOF(Stamp),reason); } static SCHEMA_UNUSED const Stamp * stamp_open( const void * bytes, uint64_t length ) { return (const Stamp *) table_cook_open( bytes, length, (uint64_t) sizeof( Stamp ), (uint64_t) SCHEMA_TABLE_ALIGNOF( Stamp ) ); } +static SCHEMA_UNUSED int schema_graphdemo_stamp_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Stamp * value=(const Stamp *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_bytes(at+0,value->tag,value->tag_length,9); + table_cook_put(at+0+12,(uint32_t)value->tag_length,4,order); + table_cook_put(at+16,(uint64_t)value->seq,4,order); + return 1; +} +static SCHEMA_UNUSED int64_t stamp_cook_measure(const Stamp * value) { (void)value; return 104; } +static SCHEMA_UNUSED int stamp_cook(const Stamp * value,void * output,uint64_t capacity,TableByteOrder order) +{ + uint8_t * raw=(uint8_t *)output; + if(value==NULL || raw==NULL || capacity<104) { return 0; } + memset(raw,0,104); + if(!schema_graphdemo_stamp_cook_body_(NULL,raw+64,value,order)) { return 0; } + table_cook_put(raw+96,UINT64_C(0xae3b9113b7db93a4),8,order); + table_cook_header(raw,SCHEMA_GRAPHDEMO_BUILD_VERSION_VALUE,24,1,8,order); return 1; +} +static SCHEMA_UNUSED int schema_graphdemo_colour_cook_body_(struct TableNumbering * n,uint8_t * at,const void * storage,int order) +{ + const Colour * value=(const Colour *)storage; + (void)n; (void)at; (void)value; (void)order; + table_cook_put(at+0,(uint64_t)value->r,1,order); + table_cook_put(at+1,(uint64_t)value->g,1,order); + table_cook_put(at+2,(uint64_t)value->b,1,order); + return 1; +} /* ---- relocatability: the wire is a pure length-prefixed stream AND the decoded storage is pointer-free — every closure type is a plain C struct of scalars and arrays, so instances can be memcpy'd, mmap'd, shared diff --git a/testdata/wire/tables/fuzz-vectors/INDEX.txt b/testdata/wire/tables/fuzz-vectors/INDEX.txt index 7ec5967c1..1932636f6 100644 --- a/testdata/wire/tables/fuzz-vectors/INDEX.txt +++ b/testdata/wire/tables/fuzz-vectors/INDEX.txt @@ -167,5 +167,8 @@ message_bytes_truncated backenddemo LoginRequest testdata/wire/tables/fuzz-vecto # The key scan reads through an incompatible occurrence; the final key kind wins. map_key_repeat_kind mapdemo Slots testdata/wire/tables/fuzz-vectors/map_key_repeat_kind.bin +# A damaged framed pointer arm cannot produce a pointee-kind mismatch. +pointer_arm_trailing streamdemo Feed testdata/wire/tables/fuzz-vectors/pointer_arm_trailing.bin + # A later map occurrence supersedes retained bodies even after a damaged key cleared its value. map_retained_replaced mapdemo Text testdata/wire/tables/fuzz-vectors/map_retained_replaced.bin diff --git a/testdata/wire/tables/fuzz-vectors/pointer_arm_trailing.bin b/testdata/wire/tables/fuzz-vectors/pointer_arm_trailing.bin new file mode 100644 index 000000000..ed7ae770d Binary files /dev/null and b/testdata/wire/tables/fuzz-vectors/pointer_arm_trailing.bin differ diff --git a/tools/sabotage/ctable_message.go b/tools/sabotage/ctable_message.go new file mode 100644 index 000000000..7954541bf --- /dev/null +++ b/tools/sabotage/ctable_message.go @@ -0,0 +1,5 @@ +package main + +func init() { + sabotages["message-c-wrong-slot"] = []edit{{old: "ind, g.messageSlot(e))", new: "ind, g.messageSlot(e)+1) // SABOTAGED: a different vocabulary slot"}} +} diff --git a/tools/sabotage/ctable_retain.go b/tools/sabotage/ctable_retain.go new file mode 100644 index 000000000..6d7f61ffb --- /dev/null +++ b/tools/sabotage/ctable_retain.go @@ -0,0 +1,5 @@ +package main + +func init() { + sabotages["retain-c-drop-field"] = []edit{{old: "table_retain_commit(keep,record,&s,r->report);return 1;", new: "return 1; /* SABOTAGED: silently drop a complete retained field */"}} +}