Security - Remove the unsafe Vec::set_len over recycled sector buffers — UB reachable from safe code (GHSA-j8ww-f5fg-9pmh, low). Uses Vec::resize; pooling unchanged. CI / tooling - clippy on stable with the MSRV single-sourced from Cargo.toml; cargo-deny dependency-audit gate with first-party git sources allowed (#15); audience-based comment-guard (#17). Docs - README (keydb ownership, BD-J parser count), drive-access transport/reset notes, mux timeline module-doc links.
3.9 KiB
src/io/sink — output-sink trait split
Two traits, one for each capability axis of an output destination:
- [
SequentialSink] — anything you canWriteto in order. Sockets, pipes, append-only stores, plain files. Containers that don't need seek (M2TS, fMP4, HEVC elementary) target this. - [
RandomAccessSink] — everythingSequentialSinkplus a workingSeek. Local files, NFS files, anything with random-write semantics. Containers that need backpatch (MKV cluster sizes, Cues index, MP4 moov-at-end) target this.
RandomAccessSink: SequentialSink — every random-access sink is
also a valid sequential sink. The muxer is generic over which it
requires (MkvMux<S: RandomAccessSink>, M2tsMux<S: SequentialSink>)
so an attempt to mux MKV to a network socket is a compile error.
Buffering policy belongs to the concrete sink, not to a wrapper at
the call site. LocalFileSink wraps a BufWriter<File> with a
4 MiB buffer for the common local-disk case; WritebackFile
(separate module) wraps a File with the adaptive-chunk
sync_file_range machinery for the Linux+NFS case.
SequentialSink::finish
finish drains any internal buffering and signals end-of-stream to
the underlying transport (close-write on a socket, flush + fsync on
a buffered file, etc.). The default impl flushes via [Write::flush]
— correct for an unbuffered destination — but every concrete sink in
this module overrides it to drain its own buffer and run its
transport-specific finalisation (socket shutdown(Write), file
fsync). There is deliberately NO blanket impl SequentialSink for T: a blanket impl would force the no-op-style default on every
concrete sink (a blanket impl cannot be overridden per-type without a
coherence conflict), so a Box<dyn SequentialSink> / &mut dyn SequentialSink finish() call would silently skip the flush and
transport shutdown. With explicit per-type impls the vtable dispatches
finish to the real implementation, so flush + durable-finish
actually happen through a trait object.
open_for_mkv
Picks the right RandomAccessSink impl for dest based on its
filesystem type.
- Linux + NFS path →
WritebackFilewith its adaptive-chunk sync_file_range machinery and (when supported)fallocatesize hint. - everything else →
LocalFileSinkoverBufWriter<File>. On non-Linux there is noWritebackFilemachinery to opt into, and on local Linux the kernel's default writeback policy is already fine.
size_hint, when present, is forwarded to the per-OS preallocate
path (fallocate(KEEP_SIZE) on Linux, F_PREALLOCATE on macOS when
implemented, no-op elsewhere).
Returns a boxed trait object so the call site (mux construction) stays agnostic of which concrete sink got picked.
Not yet wired into mux::resolve (follow-up commit). Kept
pub(crate) until then so an unfinished signature isn't frozen into
the public 1.0 API.
Test notes
finish_through_trait_object_flushes_local_fileis the regression test for the silent-no-opfinish()bug:finish()through adyn SequentialSinktrait object must dispatch to the concrete sink's override (flush + fsync), not a no-op default.FlushTrackeris a minimalSequentialSinkthat does NOT overridefinish, so it exercises the trait's default impl, which must callWrite::flush. This pins the documented contract that the defaultfinishis "correct for an unbuffered destination" by flushing. Mutation: changing the defaultfinishbody fromself.flush()toOk(())would setflushed=falseand fail.open_for_mkv_without_size_hint_is_random_accesscoversopen_for_mkvwithNonesize hint: it must still produce a working random-access sink (thematch size_hint { None => ... }arm). Round-trips a seek-back patch through it to prove both Write and Seek dispatch. Mutation: if theNonearm returned a sequential-only sink the seek would not compile / would fail.