Skip to content

Commit a89a43f

Browse files
feat: store exact homogeneous arrays statically
1 parent 91e439c commit a89a43f

58 files changed

Lines changed: 2029 additions & 388 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cddl-matrix/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,9 @@ the guard that replaced the silent truncation this row's vector exposed; pinned
193193
`signed_ints_width_rejects` / `width_collapse_rejects` execution fixtures),
194194
the eight `rangeop` rows (`rangeop.{inclusive,exclusive}` plus their head-type × sign variation
195195
rows `.int`/`.nint`/`.float`), the three occurrence-bound rows `occur.bounded{,.lower,.upper}`
196-
(out-of-count arrays against the generated bounded collection conversion door — `BoundedVec` or
197-
`BoundedOrderedSet` for reject sets), and the three fixed-value lexeme
196+
(out-of-count arrays against the generated collection handover — `BoundedVec` for variable
197+
windows, `[T; N]` conversion for exact ordinary windows, or `BoundedOrderedSet` for reject sets),
198+
and the three fixed-value lexeme
198199
rows `value.number.{hexfloat,hex,bin}` (wrong-value instances against the fixed 3.0 / 16 / 10,
199200
rejected as FixedValueMismatch — hex/bin carry hand pins including `[0]`, the silent-zero
200201
radix-conversion trap), the fixed-selector MEMBER-equality family

cddl-matrix/containment/occurrence-target.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ id = "contain.occurrence-target.grpent.member.zero_array"
260260
role = "role.occurrence-target"
261261
feature = "grpent.member"
262262
spec = "allowed"
263-
note = "A zero-or-more occurrence on a bare-type entry in a HETEROGENEOUS array is an open-array segment. A final `* t` tail is supported, as is the one safe non-final shape: a leading/middle segment whose immediate fixed suffix is mandatory, exactly one CBOR item, field-codec-free, and CBOR-major-disjoint from `t` (`[uint, * bytes, tstr]`). Each effective boundary head is generator-proven (including mandatory outer framing) or declared by a transparent custom-codec alias with `@custom_wire_major`; an undeclared custom-owned or direct opaque-extern head remains unproven. The decoder obeys RFC 8610 greedy non-backtracking matching: it consumes repeated element majors without rewind and stops at that disjoint suffix. Same-major/value-discriminator boundaries, multiple occurrences, and group/group-choice/plain-group forms stay rejected gracefully. `*` captures `Vec<T>`; `+` and bounded siblings use `NonEmptyVec`/`BoundedVec`. Under --preserve-encodings each captured segment element re-serializes byte-exactly via a positional per-element sidecar (a self-carried `any` element needs none), and --canonical-form normalizes each element in source position; the captured `Vec<T>` renders as an ordinary JSON array field (skip-if-empty on write, default-on-read) and exposes a read-only wasm `rest()` list-wrapper getter. RFC8610 §3.2, §3.4."
263+
note = "A zero-or-more occurrence on a bare-type entry in a HETEROGENEOUS array is an open-array segment. A final `* t` tail is supported, as is the one safe non-final shape: a leading/middle segment whose immediate fixed suffix is mandatory, exactly one CBOR item, field-codec-free, and CBOR-major-disjoint from `t` (`[uint, * bytes, tstr]`). Each effective boundary head is generator-proven (including mandatory outer framing) or declared by a transparent custom-codec alias with `@custom_wire_major`; an undeclared custom-owned or direct opaque-extern head remains unproven. The decoder obeys RFC 8610 greedy non-backtracking matching: it consumes repeated element majors without rewind and stops at that disjoint suffix. A finite exact `N*N` segment instead stops by count, needs no major discriminator, and stores ordinary/preserve data as `[T; N]` (exact reject remains `BoundedOrderedSet<T, N, N>`); uniquely named finite exact segments may be adjacent. Variable same-major/value-discriminator, variable multiple, and group/group-choice/plain-group forms stay rejected gracefully. `*` captures `Vec<T>`, `+` `NonEmptyVec<T>`, and variable bounded windows `BoundedVec<T, MIN, MAX>`. Under --preserve-encodings each captured segment element re-serializes byte-exactly via a positional per-element sidecar (a self-carried `any` element needs none), and --canonical-form normalizes each element in source position; the loose captured `Vec<T>` renders as an ordinary JSON array field (skip-if-empty on write, default-on-read) and exposes a read-only wasm `rest()` list-wrapper getter. RFC8610 §3.2, §3.4."
264264
example = "a = [uint, * bytes, tstr]"
265265

266266
[[contain]]

cddl-matrix/matrix.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4813,7 +4813,7 @@
48134813
"example": "a = [uint, * bytes, tstr]",
48144814
"feature": "grpent.member",
48154815
"id": "contain.occurrence-target.grpent.member.zero_array",
4816-
"note": "A zero-or-more occurrence on a bare-type entry in a HETEROGENEOUS array is an open-array segment. A final `* t` tail is supported, as is the one safe non-final shape: a leading/middle segment whose immediate fixed suffix is mandatory, exactly one CBOR item, field-codec-free, and CBOR-major-disjoint from `t` (`[uint, * bytes, tstr]`). Each effective boundary head is generator-proven (including mandatory outer framing) or declared by a transparent custom-codec alias with `@custom_wire_major`; an undeclared custom-owned or direct opaque-extern head remains unproven. The decoder obeys RFC 8610 greedy non-backtracking matching: it consumes repeated element majors without rewind and stops at that disjoint suffix. Same-major/value-discriminator boundaries, multiple occurrences, and group/group-choice/plain-group forms stay rejected gracefully. `*` captures `Vec<T>`; `+` and bounded siblings use `NonEmptyVec`/`BoundedVec`. Under --preserve-encodings each captured segment element re-serializes byte-exactly via a positional per-element sidecar (a self-carried `any` element needs none), and --canonical-form normalizes each element in source position; the captured `Vec<T>` renders as an ordinary JSON array field (skip-if-empty on write, default-on-read) and exposes a read-only wasm `rest()` list-wrapper getter. RFC8610 §3.2, §3.4.",
4816+
"note": "A zero-or-more occurrence on a bare-type entry in a HETEROGENEOUS array is an open-array segment. A final `* t` tail is supported, as is the one safe non-final shape: a leading/middle segment whose immediate fixed suffix is mandatory, exactly one CBOR item, field-codec-free, and CBOR-major-disjoint from `t` (`[uint, * bytes, tstr]`). Each effective boundary head is generator-proven (including mandatory outer framing) or declared by a transparent custom-codec alias with `@custom_wire_major`; an undeclared custom-owned or direct opaque-extern head remains unproven. The decoder obeys RFC 8610 greedy non-backtracking matching: it consumes repeated element majors without rewind and stops at that disjoint suffix. A finite exact `N*N` segment instead stops by count, needs no major discriminator, and stores ordinary/preserve data as `[T; N]` (exact reject remains `BoundedOrderedSet<T, N, N>`); uniquely named finite exact segments may be adjacent. Variable same-major/value-discriminator, variable multiple, and group/group-choice/plain-group forms stay rejected gracefully. `*` captures `Vec<T>`, `+` `NonEmptyVec<T>`, and variable bounded windows `BoundedVec<T, MIN, MAX>`. Under --preserve-encodings each captured segment element re-serializes byte-exactly via a positional per-element sidecar (a self-carried `any` element needs none), and --canonical-form normalizes each element in source position; the loose captured `Vec<T>` renders as an ordinary JSON array field (skip-if-empty on write, default-on-read) and exposes a read-only wasm `rest()` list-wrapper getter. RFC8610 §3.2, §3.4.",
48174817
"role": "role.occurrence-target",
48184818
"spec": "allowed"
48194819
},

cddl-matrix/no_std_check.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,15 +361,18 @@ const PROFILES: Profile[] = [
361361
// (the std prelude supplies the name) and an E0425 the moment the crate is built without it.
362362
// That is exactly the shape of the first consumer-reported no_std break, and this member set —
363363
// a plain `any`, an optional `any`, a `[* any]` seq, an optional seq, a `{* K => any}` table and
364-
// an optional table — is what puts every adapter shape inside the thumb compile.
364+
// an optional table — is what puts every adapter shape inside the thumb compile. `wide` makes
365+
// the separately composed typed static-array runtime live too: its child modules must spell
366+
// allocation macros explicitly rather than inheriting the host `std` prelude.
365367
id: "json_schema",
366368
libName: "nostd-json",
367369
flags: ["--preserve-encodings=true", "--json-serde-derives=true", "--json-schema-export=true"],
368370
cddl: [
369371
"inner = [ a: uint, b: text ]",
370372
"tbl = { * uint => text }",
373+
"wide = [64*64 uint]",
371374
"any_members = { 1: any, ? 2: any, 3: [* any], ? 4: [* any], 5: { * uint => any }, ? 6: { * uint => any } }",
372-
"outer = [ i: inner, t: tbl, ? o: bytes, am: any_members ]",
375+
"outer = [ i: inner, t: tbl, ? o: bytes, w: wide, am: any_members ]",
373376
"",
374377
].join("\n"),
375378
},

cddl-matrix/roadmap.toml

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -629,28 +629,30 @@ event_md = '''a concrete query needs it
629629

630630
[[record]]
631631
id = "matrix.fixed-array.static-representation"
632-
title = "Fixed-array representation optimization"
632+
title = "Compose exact static-array JSON support through containing shapes"
633633

634634
[record.payload]
635635
kind = "work"
636-
body_md = '''### Fixed-array representation optimization
636+
body_md = '''### Compose exact static-array JSON support through containing shapes
637637
638-
An exact `n*n T` can map to `[T; n]`, so the representation itself carries the constraint. The
639-
`uint .size 1` → `u8` mapping is the shipped precedent, independently of the `TryFrom` door, which
640-
stays as the ergonomic entry point. The fixed carrier and its construction door must compose with
641-
serialization, deserialization, Rust, wasm, JSON, component, cross-crate projection, matrix
642-
classification, and spec-derived boundary vectors.
638+
Exact ordinary/preserve `n*n T` occurrences now use `[T; n]` across Rust, CBOR, wasm, component,
639+
cross-crate projection, matrix classification, and boundary vectors. The pinned serde/schemars
640+
versions only derive arrays through length 32, so the generated `static_array` runtime presently
641+
adapts a direct field/newtype payload and a loose `Vec<[T; N]>` occurrence when `T` is not CDDL
642+
`any`. Direct/optional exact `any` arrays use the natural-JSON static adapter. That is not a general
643+
solution: an optional loose sequence, bounded sequence, map value, nested exact array, or another
644+
containing shape would still require `Serialize`/`Deserialize`/`JsonSchema` directly on a wide
645+
`[T; N]`. With either JSON flag, the generator now rejects those shapes loudly before codegen rather
646+
than emitting a crate that fails to compile.
643647
644648
'''
645649
work_state = "ready"
646-
work_intent = "optimize"
647-
work_kind = "optimization"
650+
work_intent = "build_capability"
651+
work_kind = "feature"
648652
risk = "wrong_public_api"
649-
acceptance_md = '''Map exact n*n homogeneous array occurrences to [T; n], retain TryFrom as the ergonomic construction door, keep fixed-byte representation outside this identity, and update every applicable Rust, wasm, JSON, component, cross-crate, matrix, and boundary-vector surface.
650-
'''
653+
acceptance_md = '''Make JSON derives and schema generation compositional for every supported position that contains a wide exact ordinary/preserve array, including optional/loose/bounded sequences, map values, nested exact arrays, records, aliases, and type choices; preserve the ordinary JSON list surface and exact inner minItems/maxItems. Preserve CDDL `any`'s natural JSON rendering rather than falling back to AnyCbor's tagged codec, including nested exact-any arrays. Add execution coverage for representative nesting families. Until then, reject every unadapted containing shape under either JSON flag before codegen; do not claim complete JSON support while any containing shape still relies on serde/schemars' length-32 array traits.'''
651654
priority_band = "normal"
652-
priority_rationale_md = '''The fixed-array representation remains explicitly selected because an exact array type carries the cardinality invariant statically and avoids conflating this scope with the separate fixed-byte representation.
653-
'''
655+
priority_rationale_md = '''The native carrier is shipped, but JSON flags must not turn otherwise-supported CDDL nesting into a generated-crate compile failure. The remaining work is an adapter-composition design, not a reason to revert the representation.'''
654656

655657
[[record]]
656658
id = "matrix.fixed-bytes.lowercase-base64-parser"
@@ -955,8 +957,10 @@ body_md = '''### Honor the remaining ambiguous/multiple count-permitting occurre
955957
956958
A final occurrence after fixed members is supported: bare `*` / `0*` uses the loose open-array
957959
`Vec` rest tail, `+` / `1*` uses its `NonEmptyVec` twin with the shipped first-element construction
958-
door, and every other bounded window uses a complete checked `BoundedVec` carrier (including finite,
959-
max-only, min-only, and exact-zero windows). One leading/middle bare-type occurrence may precede an
960+
door; variable bounded windows use a complete checked `BoundedVec` carrier (including finite,
961+
max-only, and min-only windows), while ordinary/preserve exact `N*N` homogeneous windows use the
962+
native `[T; N]` carrier (including zero). Exact `@duplicates reject` remains a checked
963+
`BoundedOrderedSet<T, N, N>`. One leading/middle bare-type occurrence may precede an
960964
immediate mandatory, single-item fixed suffix. Variable windows retain the field-codec-free,
961965
effective, CBOR-major-disjoint boundary (`[uint, * bytes, tstr]`): heads are generator-proven or a
962966
transparent custom alias declares its actual wire major. A variable same-major boundary is also
@@ -967,7 +971,7 @@ owned heads. The decoder therefore obeys RFC 8610 greedy, non-backtracking match
967971
speculation; final and safe-middle forms share the
968972
carrier/projection paths covered by `contain.occurrence-target.grpent.member.{zero_array,plus_array,bounded_array}`.
969973
Two or more occurrence members are supported when every member has a unique explicit `@name` and a
970-
finite exact `N*N` window other than `1*1`: each is a separate flat `BoundedVec` and exact count is
974+
finite exact `N*N` window other than `1*1`: each is a separate flat `[T; N]` and exact count is
971975
its wire boundary, including adjacent and same-major segments.
972976
973977
Still rejected gracefully: variable-cardinality multiple occurrence-bearing members; inline-group and group-choice-arm

0 commit comments

Comments
 (0)