# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- `BufferedSource::builder()` — fluent builder exposing every prefetch
knob (`capacity`, `block_size`, `prefetch_timeout`,
`lookback_fraction`) as a per-source setting. Previously the worker
block size (256 KiB), prefetch timeout (30 s), and lookback fraction
(1/8) were compile-time constants; a caller talking to a fast local
source or a slow satellite link had no way to tune them. The new
builder reads sensible defaults and clamps each knob on `build` so the
worker is always able to make forward progress (capacity ≥ 4 × block,
block ≥ 4 KiB, timeout ≥ 1 ms, lookback strictly less than 1).
`BufferedSource::new(inner, capacity)` keeps its historical two-arg
shape and now resolves all other knobs to their defaults via the
builder. Public constants `DEFAULT_BLOCK`,
`DEFAULT_PREFETCH_TIMEOUT`, `DEFAULT_LOOKBACK_NUM`, and
`DEFAULT_LOOKBACK_DEN` surface the defaults for callers that want to
re-derive them. `BufferedSource::prefetch_timeout()` returns the
effective timeout post-clamping for diagnostics.
## [0.1.5](https://github.com/OxideAV/oxideav-source/compare/v0.1.4...v0.1.5) - 2026-05-29
### Other
- accept mem://, data:, slice: segments alongside file://
- URI-level windowed view — slice:<offset>+<length>!<inner-uri>
- SubSource — windowed view + Arc-backed mem:// reader
- driver — concatenate file:// segments into one seekable stream
- add RFC 2397 data:[...][;base64],<bytes> driver
- make permissive() cross-platform
- add mem:// driver + FileScope allow-list for file://
### Changed
- `concat:` driver now accepts the same inner-scheme set the `slice:`
driver does — segments may be bare paths, `file://`, `mem://`, `data:`,
or `slice:` URIs (previously only bare paths and `file://` URLs were
accepted). Dispatch is done per segment via the matching bundled
opener, so the mixed-scheme case `concat:<file>|mem://<id>|data:,TAIL`
works end-to-end without first materialising the inputs as files.
Nested `concat:` segments are rejected because the outer `|` split
would shred the inner segment list; use a single flattened list
instead. Previously-rejected `concat:mem://x|mem://y` and
`concat:data:,a|data:,b` URIs now succeed.
### Added
- `slice:<offset>+<length>!<inner-uri>` scheme — URI-level windowed view
over an inner source. `open_slice` parses the decimal range header,
dispatches the inner URI to the matching bundled opener (`file://` /
bare path, `mem://`, `data:`, or another `slice:` for recursive
composition), and wraps the result in a `SubSource` that re-projects
`[offset, offset + length)` onto `[0, length)`. The `!` separator was
chosen because it never appears in `file://` paths and is not used by
the other bundled schemes, so the split is unambiguous even when the
inner URI carries its own `:` and `://`. Pipelines and CLI flags can
now address a sub-range of any in-process source without first
materialising it. `with_defaults()` and `register()` install the
`slice` driver alongside `file`, `mem`, `data`, and `concat`.
- `SubSource` — windowed view (`[base, base + len)` → `[0, len)`) over
any `Box<dyn BytesSource>`. The seekable analogue of
`std::io::Read::take`: containers can hand a codec a stream that looks
like the codec's own sample, including support for seeking back inside
the window (e.g. re-reading a header after probing). Bounds are
validated at construction via a non-destructive end-seek probe;
zero-length windows, exact-tail windows, and nested windows all
compose. Helper `stream_len(&mut dyn BytesSource) -> io::Result<u64>`
probes the inner length without disturbing the cursor.
- `concat:<a>|<b>|…` scheme — concatenate several `file://` segments into
one seekable `BytesSource`. `open_concat` opens each `|`-separated
segment with the `file` driver (bare paths and `file://` URLs both
accepted), captures each segment length at open time, and presents the
composite over the virtual address space `[0, total_len)`: `Read`
walks segment boundaries transparently and `Seek`
(`Start`/`End`/`Current`) resolves an absolute offset into the right
segment. Empty segments (`a||b`, trailing `|`) and an empty payload
are rejected. `with_defaults()` and `register()` now install the
`concat` driver alongside `file`, `mem`, and `data`.
- `data:[<mediatype>][;base64],<bytes>` scheme — RFC 2397 inline byte
literals decoded directly from the URI (no filesystem access).
`open_data` returns a `Cursor`-backed `BytesSource`; `parse_data_uri`
surfaces the parsed `DataUri { mediatype, base64, data }` for callers
that need to route on media type. `with_defaults()` and `register()`
now install the `data` driver alongside `file` and `mem`. Percent
decoding is default; the `;base64` marker (case-insensitive) switches
to RFC 4648 §4 base64 with internal whitespace tolerated.
- `mem://<id>` scheme — in-memory buffer registry (`mem::put` / `mem::remove` / `mem::clear`) and `open_mem` opener. `with_defaults()` now installs both the `file` and `mem` drivers.
- `FileScope` — directory allow-list for the `file://` driver. Resolves
requests through `std::fs::canonicalize` (defeats `../` traversal via
symlinks), then rejects anything outside the canonicalised allow-list
with component-aware prefix matching. Install with
`FileScope::register_into(&mut SourceRegistry)`.
### Changed
- `BufferedSource::read` uses `VecDeque::as_slices` + `copy_from_slice`
for the ring → out copy instead of an element-wise loop, so a
million-byte hit no longer iterates byte-by-byte under the lock.
- `register()` now installs both `file` and `mem` drivers into the
passed `RuntimeContext`.
- `open_mem` returns an `Arc<Vec<u8>>`-backed `Read + Seek` reader
instead of a fresh `Cursor<Vec<u8>>` cloned from the buffer. Multiple
concurrent opens of the same id now share the bytes by reference; the
per-open cost drops from a full `Vec<u8>` copy to a single `Arc`
clone. Reader semantics are unchanged: each handle owns its own
position, so reads on different handles are still independent.
## [0.1.4](https://github.com/OxideAV/oxideav-source/compare/v0.1.3...v0.1.4) - 2026-05-06
### Other
- reframe FFI claim — HW-engine crates use OS FFI by necessity
- drop dead `linkme` dep
- auto-register via oxideav_core::register! macro (linkme distributed slice)
- replace never-match regex with semver_check = false
- migrate to centralized OxideAV/.github reusable workflows
## [0.1.3](https://github.com/OxideAV/oxideav-source/compare/v0.1.2...v0.1.3) - 2026-05-02
### Other
- stay on 0.1.x during heavy dev (semver_check=false)
- Migrate file:// driver to SourceRegistry typed-bytes API
- pin release-plz to patch-only bumps
### Changed
- **Breaking**: Migrated to the new typed `SourceRegistry` API in
`oxideav-core`. The `file://` driver now registers via
`register_bytes("file", open_file)` (was `register("file", …)`),
and `open_file` returns `Box<dyn BytesSource>` (was `Box<dyn
ReadSeek>`). `BytesSource` is blanket-implemented for every
`Read + Seek + Send` type so the underlying `File` shape is unchanged.
`with_defaults()` / `register(&mut RuntimeContext)` keep their
signatures; callers of `reg.open(uri)` now match a `SourceOutput`
enum and bind the `Bytes` variant to get the reader back.
- Re-exports updated: `BytesSource`, `PacketSource`, `FrameSource`,
`SourceOutput` are now surfaced from this crate alongside
`SourceRegistry`. The `OpenSourceFn` re-export is removed.
## [0.1.2](https://github.com/OxideAV/oxideav-source/compare/v0.1.1...v0.1.2) - 2026-04-25
### Other
- drop "future" qualifier on http://
- release v0.1.1
## [0.1.1](https://github.com/OxideAV/oxideav-source/compare/v0.1.0...v0.1.1) - 2026-04-25
### Other
- re-export SourceRegistry from oxideav-core; expose register fn
- release v0.0.4
## [0.1.0](https://github.com/OxideAV/oxideav-source/compare/v0.0.3...v0.1.0) - 2026-04-19
### Other
- bump version to 0.1.0
- bump oxideav-container dep to "0.1"
- drop Cargo.lock — this crate is a library