Release v0.3.0: compiled forms — ReadPlan, owned BastDoc, LeafMeta, ValidationPlan, fingerprinting
Public API bump 0.2.0 -> 0.3.0 (the 030-compiled-forms plan is now fully implemented; all eight phases landed). - Cargo.toml: version 0.3.0. lib.rs re-exports complete (ReadPlan + sub-types, LeafMeta, OffsetEntry, ValidationPlan + sub-types). - ADR-007 "Cost" rewritten to the Arc<ReadPlan> cost (15.7 ns) with the 0.2.0 "re-parse on demand" framing as a historical note (review #004 L2, the last loose end from that review). - ADR-011/012 status blocks flipped to implemented; architecture README ADR table rows updated; layout-engine.md rewritten for the 0.3.0 surface (engine-factory reader construction, OffsetMap OffsetEntry/LeafMeta/fingerprint section, owned BastDoc compute signature); SequentialReader module doc points at the engine factory. Reviews #004 and #005 flipped to closed. - Bench re-run (alktty wire_vs_bast, 0.3.0 tree): read p64 98 ns/chunk (parity with phase 2; hand-rolled 5.7 us/stream), layout_build 180 ns (was ~1.2 us — the phase-4 owned-doc cache removed the per-build re-parse, ~7x), sequential_reader_new 15.7 ns, write p64 -3%, engine_compile unchanged (meta-schema validation dominates). No dedicated validate_bytes-stream bench: the phase-7 spot check (~0.2 us plan-validate vs ~0.6 us compile-per-call) stands; a dedicated bench is a follow-up if alkcall profiling motivates it. - Downstream: alktty compiles against the path dep unchanged; alkcall has no dependency yet. Verification (full block, all green): 474 tests; clippy -D warnings clean; cargo doc zero warnings; wasm32 release build green; cargo publish --dry-run clean at 0.3.0.
This commit is contained in:
1 parent
537a2170fb
commit
9949f914df
11 files changed
+94
-36
No files matched your search
@@ -45,8 +45,8 @@ format definition; the engine is generic.
|
||||
| [008](decisions/008-reject-tunion-in-aligned-mode.md) | Reject TUnion in Aligned Mode for v1 | Unions are the protocol pattern; aligned-mode union semantics were broken |
|
||||
| [009](decisions/009-builder-api.md) | Builder API for Schema Construction | Fluent Rust API producing `serde_json::Value`; covers BAST kinds + standard JSON Schema; resolves OQ-003. *Output format amended to BAST / standard JSON Schema by ADR-BAST.* |
|
||||
| [010](decisions/010-generalized-validation-validate-bytes.md) | Generalized Validation — `validate_bytes` on `AlkTypeEngine` | Single-call binary-buffer validation; materialize `Value` from bytes, then validate. *Validation step amended to the BAST-native validator by ADR-VAL-SPLIT.* |
|
||||
| [011](decisions/011-compiled-read-plan-for-packed-mode.md) | Compiled Read Plan for Packed Mode | `ReadPlan` — the packed read-side compiled form, symmetric to `OffsetMap` (aligned) and `PackedLayout` (packed write). Closes review #004's 400x read-path gap; retires ADR-007's "re-parse on demand" framing. *Accepted.* |
|
||||
| [012](decisions/012-plan-fingerprinting-and-m1-closure.md) | Plan Fingerprinting, ValidationPlan, and Closing the Deferred M1 Sites in 0.3.0 | `ReadPlan`/`OffsetMap`/`ValidationPlan` `Hash + Eq` + `fingerprint()`; owned `BastDoc` (lifetime removal); `OffsetMap` carries `LeafMeta` to close the aligned-side M1 sites; `ValidationPlan` retires the interpretive `bast_validation` walk (review #005 M3 reversed the original deferral). Bundles with ADR-011 into one 0.3.0 breaking release. *Accepted — §3a shape implemented in `src/validation_plan.rs`.* |
|
||||
| [011](decisions/011-compiled-read-plan-for-packed-mode.md) | Compiled Read Plan for Packed Mode | `ReadPlan` — the packed read-side compiled form, symmetric to `OffsetMap` (aligned) and `PackedLayout` (packed write). Closes review #004's 400x read-path gap; retires ADR-007's "re-parse on demand" framing. *Accepted — implemented in 0.3.0 (phases 1–2).* |
|
||||
| [012](decisions/012-plan-fingerprinting-and-m1-closure.md) | Plan Fingerprinting, ValidationPlan, and Closing the Deferred M1 Sites in 0.3.0 | `ReadPlan`/`OffsetMap`/`ValidationPlan` `Hash + Eq` + `fingerprint()`; owned `BastDoc` (lifetime removal); `OffsetMap` carries `LeafMeta` to close the aligned-side M1 sites; `ValidationPlan` retires the interpretive `bast_validation` walk (review #005 M3 reversed the original deferral). Bundles with ADR-011 into one 0.3.0 breaking release. *Accepted — fully implemented in 0.3.0 (fingerprinting, owned `BastDoc`, `LeafMeta`, `ValidationPlan`).* |
|
||||
|
||||
## Relevant Open Questions
|
||||
|
||||
|
||||
@@ -63,23 +63,26 @@ write-side.
|
||||
|
||||
### Cost
|
||||
|
||||
`SequentialReader::new` clones the top-level struct's field schemas (a
|
||||
`Vec<(String, Value)>` of the `properties` entries) and clones the
|
||||
schema itself. This is cheap — a struct has a small number of fields
|
||||
(SFTP's largest packet has 5). The construction cost is negligible
|
||||
compared to the cost of reading a buffer.
|
||||
`SequentialReader::new(Arc<ReadPlan>)` is a refcount bump — 15.7 ns
|
||||
(measured, alktty `wire_vs_bast` bench, 0.3.0). The reader shares the
|
||||
engine's compiled [`ReadPlan`](011-compiled-read-plan-for-packed-mode.md)
|
||||
(the packed read-side compiled form) via `Arc` instead of cloning
|
||||
schema data; construction cost is negligible compared to reading a
|
||||
buffer. The engine holds the owned `BastDoc` (ADR-012 §2a) for the
|
||||
aligned materialize path and the one-shot `*::compile` paths.
|
||||
|
||||
> **Note**: The "re-parse on demand" framing below (the read loop
|
||||
> re-parsing `BastDoc::new` per field) is the root cause of the 400x
|
||||
> read-path gap measured in
|
||||
> **Historical note**: the original 0.2.0 framing here ("re-parse on
|
||||
> demand" — the read loop re-parsing `BastDoc::new` per field) was the
|
||||
> root cause of the 400x read-path gap measured in
|
||||
> [review #004](../../reviews/004-performance-review.md).
|
||||
> [ADR-011](011-compiled-read-plan-for-packed-mode.md) (Proposed)
|
||||
> retires this framing by giving the packed read path a compiled
|
||||
> `ReadPlan`; the "Cost" section here and the
|
||||
> `src/engine.rs:112-115` doc comment will be updated in the
|
||||
> implementation commit per ADR-011's recommended order. The factory
|
||||
> decision itself (`sequential_reader() -> Option<SequentialReader>`,
|
||||
> owned fresh reader, consumer-driven cursor) is retained.
|
||||
> [ADR-011](011-compiled-read-plan-for-packed-mode.md) (implemented,
|
||||
> 0.3.0) retired it: the packed read loop walks `Arc<ReadPlan>` (2.27
|
||||
> µs/chunk → 98 ns/chunk), `sequential_reader()` is an `Arc::clone`,
|
||||
> and the owned `BastDoc` (ADR-012 §2a) removed the remaining
|
||||
> per-access re-parse sites in `read_field`/`write_field`/`validate_bytes`.
|
||||
> The factory decision itself (`sequential_reader() ->
|
||||
> Option<SequentialReader>`, owned fresh reader, consumer-driven
|
||||
> cursor) was retained unchanged.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
## Status
|
||||
|
||||
Accepted. Closes review #004 H1 + M1 (packed side) + L1 + L2;
|
||||
Accepted. Implemented in 0.3.0 (phases 1–2, 2026-09-02). Closes review
|
||||
#004 H1 + M1 (packed side) + L1 + L2;
|
||||
retires the "re-parse on demand" framing from ADR-007. A derisking
|
||||
POC on branch `readplan-poc` confirmed the `ReadPlan` shape covers
|
||||
every `BastType` arm in the current read loop before implementation
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
## Status
|
||||
|
||||
Accepted. Implemented (§3's `ValidationPlan`, 0.3.0 phase 7, 2026-08-31);
|
||||
bundles three pieces of work into the 0.3.0 release so the
|
||||
Accepted. Implemented in 0.3.0 — §3's `ValidationPlan` (phase 7,
|
||||
2026-08-31), §1's fingerprinting (phase 6), §2a's owned `BastDoc`
|
||||
(phases 3–4), §2b's `LeafMeta` (phase 5); all shipped 2026-09-02.
|
||||
Bundles three pieces of work into the 0.3.0 release so the
|
||||
crate ships one round of breaking changes, not two (or three). The
|
||||
three pieces: (a) fingerprinting `ReadPlan`/`OffsetMap`, (b) closing
|
||||
the deferred M1 sites via an owned `BastDoc` + `OffsetMap` `LeafMeta`,
|
||||
|
||||
@@ -25,7 +25,7 @@ protocols.
|
||||
**Components:**
|
||||
|
||||
- **`LayoutBuilder`** — constructed via `LayoutBuilder::new(bast_doc, root_name)` (requires a `struct` at the root), then `builder.build(&var_sizes) -> Result<PackedLayout, AlkTypeError>` where `var_sizes: &HashMap<String, usize>` maps variable-length field paths (and TUnion discriminator/variant keys) to their actual byte sizes. Used at write time when the consumer knows the data sizes upfront. The builder computes positions only; the consumer writes data via the [`data_access`](data-access.md) functions at the computed positions.
|
||||
- **`SequentialReader`** — constructed via `SequentialReader::new(bast_doc, root_name)`, then driven by `reader.read_next(&buffer) -> Result<Option<(String, FieldValue)>, AlkTypeError>` until `Ok(None)`, or `reader.read_field(&buffer, path)` to seek a single field (which walks all preceding fields to reach the target). `reader.reset()` rewinds to the start. Used at read time when the consumer is parsing an incoming frame.
|
||||
- **`SequentialReader`** — constructed via `engine.sequential_reader()` (shares the engine's compiled `ReadPlan` via `Arc` — see [ADR-011](decisions/011-compiled-read-plan-for-packed-mode.md)), then driven by `reader.read_next(&buffer) -> Result<Option<(String, FieldValue)>, AlkTypeError>` until `Ok(None)`, or `reader.read_field(&buffer, path)` to seek a single field (which walks all preceding fields to reach the target). `reader.reset()` rewinds to the start. Used at read time when the consumer is parsing an incoming frame.
|
||||
|
||||
**How it works:**
|
||||
|
||||
@@ -69,7 +69,7 @@ and safetensors.
|
||||
|
||||
**Component:**
|
||||
|
||||
- **`OffsetMap`** — constructed via `OffsetMap::compute(&doc) -> Result<Self, AlkTypeError>` (requires a `struct` at the root). Walks the BAST typed tree once, computes fixed byte positions for each field based on type sizes and alignment. The output is a flat table of `(field_path, byte_range)` pairs (see [Public Types](#public-types)). Used for both read and write at known offsets.
|
||||
- **`OffsetMap`** — constructed via `OffsetMap::compute(&doc) -> Result<Self, AlkTypeError>` (requires a `struct` at the root). Walks the BAST typed tree once, computes fixed byte positions for each field based on type sizes and alignment, and resolves each leaf's `LeafMeta` (kind, encoding, effective endianness — ADR-012 §2b). The output is a flat table of `(field_path, OffsetEntry)` pairs (see [Public Types](#public-types)). Used for both read and write at known offsets.
|
||||
|
||||
**How it works:**
|
||||
|
||||
@@ -312,22 +312,46 @@ discriminators, the discriminator is recorded under the synthetic path
|
||||
(schema `properties` order, with nested struct fields appearing inline
|
||||
under their parent's path prefix).
|
||||
|
||||
### `LeafMeta` / `OffsetEntry` (aligned mode, 0.3.0)
|
||||
|
||||
```rust
|
||||
pub struct LeafMeta {
|
||||
pub kind: AlkTypeKind,
|
||||
pub encoding: VariableEncoding,
|
||||
pub endian: Endian,
|
||||
}
|
||||
|
||||
pub struct OffsetEntry {
|
||||
pub range: ByteRange,
|
||||
pub meta: LeafMeta,
|
||||
}
|
||||
```
|
||||
|
||||
`OffsetMap::compute` resolves each leaf's read/write metadata (kind,
|
||||
variable-length encoding, effective endianness — field override else
|
||||
container default, propagated the aligned-materializer way) alongside
|
||||
its byte range, so `read_field`/`write_field` dispatch on the entry
|
||||
without re-walking the BAST tree per access (ADR-012 §2b).
|
||||
|
||||
### `OffsetMap` (aligned mode)
|
||||
|
||||
A flat table of `(field_path, byte_range)` pairs computed from a schema.
|
||||
A flat table of `(field_path, OffsetEntry)` pairs computed from a schema.
|
||||
|
||||
```rust
|
||||
impl OffsetMap {
|
||||
pub fn compute<'a>(doc: &'a BastDoc<'a>) -> Result<Self, AlkTypeError>;
|
||||
pub fn get(&self, field_path: &str) -> Option<&ByteRange>;
|
||||
pub fn compute(doc: &BastDoc) -> Result<Self, AlkTypeError>;
|
||||
pub fn get(&self, field_path: &str) -> Option<&OffsetEntry>;
|
||||
pub fn total_size(&self) -> usize;
|
||||
pub fn iter(&self) -> impl Iterator<Item = &(String, ByteRange)>;
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &OffsetEntry)>;
|
||||
pub fn fingerprint(&self) -> u64;
|
||||
}
|
||||
```
|
||||
|
||||
`compute` requires a `struct` at the root. `total_size`
|
||||
includes trailing alignment padding. `iter` yields fields in the BAST
|
||||
`fields` array order (nested struct fields appearing inline).
|
||||
`fields` array order (nested struct fields appearing inline). The map
|
||||
carries `Hash + Eq` (ADR-012 §1); `fingerprint()` is the
|
||||
stable-within-version hash for caching and schema handshakes.
|
||||
|
||||
## Design Decisions
|
||||
|
||||
|
||||
Reference in new issue
Block a user