use std::fmt;
pub trait Source {
fn len(&self) -> u64;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, SourceError>;
}
impl Source for [u8] {
fn len(&self) -> u64 {
self.len() as u64
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, SourceError> {
let Ok(start) = usize::try_from(offset) else {
return Ok(0);
};
let Some(available) = self.get(start..) else {
return Ok(0);
};
let n = available.len().min(buf.len());
if let (Some(dst), Some(src)) = (buf.get_mut(..n), available.get(..n)) {
dst.copy_from_slice(src);
}
Ok(n)
}
}
impl Source for Vec<u8> {
fn len(&self) -> u64 {
self.as_slice().len() as u64
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, SourceError> {
self.as_slice().read_at(offset, buf)
}
}
impl<S: Source + ?Sized> Source for &S {
fn len(&self) -> u64 {
(**self).len()
}
fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, SourceError> {
(**self).read_at(offset, buf)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceError {
message: String,
}
impl SourceError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for SourceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "source read failed: {}", self.message)
}
}
impl std::error::Error for SourceError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_a_range() {
let bytes: &[u8] = b"hello world";
let mut buf = [0u8; 5];
assert_eq!(bytes.read_at(6, &mut buf), Ok(5));
assert_eq!(&buf, b"world");
}
#[test]
fn short_read_at_end() {
let bytes: &[u8] = b"abc";
let mut buf = [0u8; 8];
assert_eq!(bytes.read_at(1, &mut buf), Ok(2));
assert_eq!(&buf[..2], b"bc");
}
#[test]
fn offset_past_end_reads_nothing() {
let bytes: &[u8] = b"abc";
let mut buf = [0u8; 4];
assert_eq!(bytes.read_at(3, &mut buf), Ok(0));
assert_eq!(bytes.read_at(u64::MAX, &mut buf), Ok(0));
}
#[test]
fn empty_buffer_reads_nothing() {
let bytes: &[u8] = b"abc";
assert_eq!(bytes.read_at(0, &mut []), Ok(0));
}
#[test]
fn vec_and_reference_delegate() {
let bytes = b"xyz".to_vec();
let by_ref = &bytes;
let mut buf = [0u8; 3];
assert_eq!(Source::len(&by_ref), 3);
assert!(!Source::is_empty(&by_ref));
assert_eq!(by_ref.read_at(0, &mut buf), Ok(3));
assert_eq!(&buf, b"xyz");
assert!(Source::is_empty(&Vec::new()));
}
#[test]
fn error_displays_message() {
let err = SourceError::new("disk on fire");
assert_eq!(err.message(), "disk on fire");
assert_eq!(err.to_string(), "source read failed: disk on fire");
}
}