Commit df963f8
committed
feat(pr-x12): A1 — CTU carrier + quad-tree partition
Worker A1 of PR-X12 per .claude/knowledge/pr-x12-codec-x265-design.md
§ "Worker decomposition" line 195. Ships the structural foundation of
the cognitive-cell codec — the carrier type, quad-tree partition with
arena-backed children, leaf-CU mode taxonomy. Subsequent workers
(A2-A8: mode tags, predict, transform, quantise, RDO, rANS, stream)
consume only A1's `Ctu` type + `crate::hpc::linalg::*`.
Surface (`crate::hpc::codec::*`, feature-gated):
pub struct Ctu { block_row, block_col, tier, split_depth, arena }
pub struct CtuArena { Vec<CtuPartition>, capacity 85 (= 1+4+16+64) }
pub enum CtuPartition { Leaf(LeafCu), Split([NodeIdx; 4]) }
pub struct LeafCu { mode, basin_idx, delta?, merge_dir?, escape_idx? }
pub enum CellMode { Skip = 00, Merge = 01, Delta = 10, Escape = 11 }
pub enum MergeDir { North, East, West, South }
pub struct NodeIdx(pub u16);
impl Ctu {
pub fn new_skip(row, col, tier, basin) -> Self
pub fn split(node, depth) -> Result<[NodeIdx;4], SplitError>
pub fn merge(node) -> Result<(), MergeError>
}
Key design choices:
- **Arena-allocated quad-tree** (`CtuArena` over `Vec<CtuPartition>`
with `with_capacity(MAX_QUAD_TREE_NODES = 85)`) — matches the design
doc's stack-arena pattern + PR-X10 invariant 1 (zero-cost
abstractions, no `Box<dyn>` in hot paths). All node references are
`NodeIdx(u16)`, never raw pointers. No further heap allocation can
happen during split — the cap is structural (depth 3 × 4-way
branching = 85 max).
- **Repr-stable `CellMode` / `MergeDir`** — discriminants match the
on-wire 2-bit codes the A7 rANS encoder will emit. Test pins this
ABI (`cell_mode_discriminants_match_wire_codes`).
- **Per-mode `Option<…>` fields on `LeafCu`** — clarity-first; the
fixed per-mode bit budget collapse happens in A7 (out of scope).
- **Constructor pattern** — `LeafCu::{skip, delta, merge, escape}`
enforce "only the field for the current mode is `Some`" at the type
level so consumers can't accidentally produce a `Skip` leaf carrying
a stale `delta: Some(_)`.
- **Split / merge** — split refuses non-leaves (`NotALeaf`) and depth
beyond `MAX_SPLIT_DEPTH` (with cap returned in the error variant);
merge refuses non-Splits (`NotASplit`), inner-Split children
(`ChildNotLeaf`), and heterogeneous-mode children
(`ChildrenDiverge`). Merge does NOT compact the arena — orphaned
child nodes remain; a GC pass is out of scope for A1.
13 tests cover:
new_skip_creates_root_leaf, split_root_yields_four_children,
split_at_max_depth_rejects, split_already_split_node_rejects,
merge_homogeneous_children_collapses, merge_heterogeneous_children_rejects,
merge_split_child_rejects, merge_leaf_rejects,
leaf_constructors_set_correct_fields,
arena_capacity_bound_85 (depth-3 recursive split → exactly 85 nodes),
cell_mode_discriminants_match_wire_codes,
merge_dir_discriminants_match_wire_codes,
node_idx_root_is_zero.
Cargo.toml: new `codec = ["std"]` feature alongside `splat3d`.
src/hpc/mod.rs: `#[cfg(feature = "codec")] pub mod codec;`.
Verified locally:
cargo test -p ndarray --features codec --lib hpc::codec 13 passed
cargo check -p ndarray clean (no-codec build)
cargo fmt --check clean
cargo clippy -p ndarray --features codec -- -D warnings clean
cargo clippy --features approx,serde,rayon -- -D warnings clean
Out of scope (A2-A8 sprints):
- A2 mode.rs — 2-bit mode bit-pack/unpack helpers
- A3 predict.rs — intra/inter prediction
- A4 transform.rs — optional DCT for delta residuals
- A5 quantize.rs — 8-bit scalar quantiser
- A6 rdo.rs — λ-RDO loop
- A7 ans.rs — rANS entropy coder
- A8 stream.rs — byte-stream pack/unpack1 parent 94494bf commit df963f8
4 files changed
Lines changed: 666 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
247 | 247 | | |
248 | 248 | | |
249 | 249 | | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
250 | 255 | | |
251 | 256 | | |
252 | 257 | | |
| |||
0 commit comments