Skip to main content

BlockRead

Trait BlockRead 

Source
pub trait BlockRead: Send + Sync {
    // Required methods
    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>;
    fn size_bytes(&self) -> u64;
}
Expand description

Read-only random-access block device.

Required Methods§

Source

fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>

Read exactly buf.len() bytes starting at offset (bytes from the start of the device).

Source

fn size_bytes(&self) -> u64

Total device size in bytes. Used for bounds checks.

§It must not change for the life of the device — except through

BlockDevice::set_len

Callers are entitled to read it once, act on it, and read it again later expecting the same answer — crate::CachingDevice does exactly that, bounding a read against it and then clamping each block it fetches against it. A device whose size moves between the two makes the cache hold a block shorter than the read it was fetched for.

An implementation whose length changes UNDERNEATH IT — a second handle appending to the same file, a volume grown by something else — must still not follow it. Reopen instead. That is rust-fs-core#70, and tests/size_stability_contract.rs measures it: a FileDevice whose backing file grows through another handle goes on reporting the length it was opened with.

§THE ONE DOOR, AND WHY IT IS NOT A HOLE IN THE CONTRACT

BlockDevice::set_len moves this number, and it is the only thing that may. The distinction is not “the size is stable except when it is not”: it is that a caller ASKED, so the moment the number moves is a moment the caller chose, on a call it made, and every wrapper on the path is given the chance to keep its own view consistent as it goes past. size_bytes never surprises a reader; set_len is the reader.

That is exactly what a device following its backing file could not offer. Nobody is told, nothing is invalidated, and the two halves of one device end up disagreeing about where it ends — which is the defect, not the movement.

A device that answers false to BlockDevice::can_grow cannot be moved at all, and the stronger, older reading of this contract holds for it unchanged. Most devices in this crate are in that class.

§What the implementations here actually do

This used to say “every implementation in this crate takes its size once, at construction”. That was not true of any of the three shapes below, and it is the sentence an implementer in a sibling crate reads before deciding their own device may report a live length. crate::caching_device’s resizing path and tests/caching_size_change.rs both used to describe this differently again, so the crate said three incompatible things about one contract.

  • crate::FileDevice and the slice devices in crate::slice do take their size once, at construction. The slices keep it forever; a FileDevice opened read-write on a regular file also answers BlockDevice::can_grow with true, so its number moves when — and only when — somebody calls BlockDevice::set_len on it.
  • crate::ReadOnlyDevice, crate::CountingDevice and crate::CachingDevice FORWARD the question to the device they wrap, on every call, and so are exactly as stable as it is. They cannot be more: a wrapper has no way to hold a moving device still. The Arc<T>, Box<T> and &T blanket impls below forward the same way.
  • crate::CallbackDevice keeps its size in a PUBLIC field, so nothing stops a caller moving it after construction. Stability there is the caller’s to keep, not the type’s to enforce.

So the contract binds the implementer; it is not something this crate’s types guarantee on their behalf. tests/ size_stability_contract.rs tests each of these behaviours rather than leaving this paragraph to be believed.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementations on Foreign Types§

Source§

impl<T: BlockRead + ?Sized> BlockRead for &T

Source§

fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>

Source§

fn size_bytes(&self) -> u64

Source§

impl<T: BlockRead + ?Sized> BlockRead for Arc<T>

Source§

fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>

Source§

fn size_bytes(&self) -> u64

Source§

impl<T: BlockRead + ?Sized> BlockRead for Box<T>

Source§

fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()>

Source§

fn size_bytes(&self) -> u64

Implementors§