# Update Log
## 2026-07-03 — v0.2.6 → v0.3.0
~5,500 lines changed across 31 files. This release marks the API maturing past the
"add everything" phase: conversion is gated through a single `ConvertReader` rather
than a scatter of per-mode shortcut methods, and the public surface is tidied up
with inherent forwarding.
### Breaking API Changes
| Removed | Replacement |
|---------|-------------|
| `reader.slices_f32()` | `reader.convert::<f32>().slices()` |
| `reader.slabs_f32()` | `reader.convert::<f32>().slabs(k)` |
| `reader.read_volume_f32()` | `reader.convert::<f32>().read_volume()` |
| `slices_f32_body!` macro (internal) | `convert_iter` + `RawRegionIter` |
| `ReaderExt` trait (mid-refactor artifact) | inherent methods via `impl_reader_forwarding!` |
### New Features
- **Unified conversion API** — `ConvertReader` gating all mode conversions through
a single `.convert::<T>()` entry point, with builder-style configuration for
complex strategy and M0 interpretation
- **Built-in `ndarray` support** — `reader.to_ndarray::<T>()` returns
`ndarray::Array3<T>` when the `ndarray` feature is enabled
- **Extended header parsers** — typed parsing for CCP4 symmetry records, MRCO
legacy records, SerialEM records (with `alpha_tilt` field), and Agard records
- **Inherent forwarding** — all `ReaderMethods` / `ConvertMethods` methods are
available as inherent methods on `Reader` and `MmapReader`; no trait imports
needed for normal use
- **SIMD expansion** — added u16→f32, u8→f32, f16↔f32, byte-swap (2/4/8 byte),
and f32 statistics kernels; all with runtime feature detection
- **Criterion benchmarks** — `benches/bench.rs` covering slices, mmap, write,
stats, and conversion hot paths
- **Integration test suite** — `tests/integration.rs` with ~21 roundtrip tests
for all modes, compression, subregion reads, and edge cases
### Refinements
- **`#[cold]` on hot-path error branches** — `cold_bounds_error()` helper in
`encode_block_to_buf` / `write_block_bytes` hints LLVM to sink error branches
- **Removed `TypeId` + `unsafe` pattern** — `update_running_stats_f32()` eliminated;
`update_header_stats()` now always reads from disk (Writer), mmap (MmapWriter),
or in-memory buffer (CompressedWriter). This removes the `std::any::TypeId` trick
and the `unsafe { core::slice::from_raw_parts }` transmute
- **Added `clippy::perf` to deny** alongside `unwrap_used` and `expect_used`
### Doc Overhaul
- **README.md** — Fixed stale roadmap (CCP4/MRCO/SERI/AGAR were marked undone but
are implemented). Added extended header parsers to the feature list.
- **AGENTS.md** — Synced dep versions to `Cargo.toml`. Updated clippy reference.
Bumped test counts (~80→~91 unit, clarified doc-test count).
- **APIs.md** — Added disk-read performance note on `Writer::update_header_stats()`.
### Testing
91 unit tests, 21 integration tests, 33 doc-tests, clippy — all pass cleanly across
debug, release, and --all-features builds.
### References
- MRC2014 Specification: https://www.ccpem.ac.uk/mrc-format/mrc2014/