Skip to main content

otf_pdf_syntax/
source.rs

1//! Random-access byte input.
2
3use std::fmt;
4
5/// Random-access bytes a document is read from.
6///
7/// Core crates never open files or sockets themselves (docs/PLAN.md ยง2, "No
8/// ambient I/O"); the caller hands them a `Source` backed by memory, a file,
9/// or anything else that can serve ranges.
10pub trait Source {
11    /// Total length in bytes.
12    fn len(&self) -> u64;
13
14    /// Whether the source holds no bytes.
15    fn is_empty(&self) -> bool {
16        self.len() == 0
17    }
18
19    /// Copies bytes starting at `offset` into `buf` and returns how many were
20    /// copied. Fewer than `buf.len()` means the end of the source was reached;
21    /// an `offset` at or past the end copies nothing.
22    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, SourceError>;
23}
24
25impl Source for [u8] {
26    fn len(&self) -> u64 {
27        // `usize` is at most 64 bits on every supported target.
28        self.len() as u64
29    }
30
31    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, SourceError> {
32        let Ok(start) = usize::try_from(offset) else {
33            return Ok(0);
34        };
35        let Some(available) = self.get(start..) else {
36            return Ok(0);
37        };
38        let n = available.len().min(buf.len());
39        if let (Some(dst), Some(src)) = (buf.get_mut(..n), available.get(..n)) {
40            dst.copy_from_slice(src);
41        }
42        Ok(n)
43    }
44}
45
46impl Source for Vec<u8> {
47    fn len(&self) -> u64 {
48        self.as_slice().len() as u64
49    }
50
51    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, SourceError> {
52        self.as_slice().read_at(offset, buf)
53    }
54}
55
56impl<S: Source + ?Sized> Source for &S {
57    fn len(&self) -> u64 {
58        (**self).len()
59    }
60
61    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<usize, SourceError> {
62        (**self).read_at(offset, buf)
63    }
64}
65
66/// A [`Source`] failed to serve a read.
67#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct SourceError {
69    message: String,
70}
71
72impl SourceError {
73    /// An error carrying the source's own description of what went wrong.
74    pub fn new(message: impl Into<String>) -> Self {
75        Self {
76            message: message.into(),
77        }
78    }
79
80    /// The source's description of what went wrong.
81    pub fn message(&self) -> &str {
82        &self.message
83    }
84}
85
86impl fmt::Display for SourceError {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        write!(f, "source read failed: {}", self.message)
89    }
90}
91
92impl std::error::Error for SourceError {}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn reads_a_range() {
100        let bytes: &[u8] = b"hello world";
101        let mut buf = [0u8; 5];
102        assert_eq!(bytes.read_at(6, &mut buf), Ok(5));
103        assert_eq!(&buf, b"world");
104    }
105
106    #[test]
107    fn short_read_at_end() {
108        let bytes: &[u8] = b"abc";
109        let mut buf = [0u8; 8];
110        assert_eq!(bytes.read_at(1, &mut buf), Ok(2));
111        assert_eq!(&buf[..2], b"bc");
112    }
113
114    #[test]
115    fn offset_past_end_reads_nothing() {
116        let bytes: &[u8] = b"abc";
117        let mut buf = [0u8; 4];
118        assert_eq!(bytes.read_at(3, &mut buf), Ok(0));
119        assert_eq!(bytes.read_at(u64::MAX, &mut buf), Ok(0));
120    }
121
122    #[test]
123    fn empty_buffer_reads_nothing() {
124        let bytes: &[u8] = b"abc";
125        assert_eq!(bytes.read_at(0, &mut []), Ok(0));
126    }
127
128    #[test]
129    fn vec_and_reference_delegate() {
130        let bytes = b"xyz".to_vec();
131        let by_ref = &bytes;
132        let mut buf = [0u8; 3];
133        assert_eq!(Source::len(&by_ref), 3);
134        assert!(!Source::is_empty(&by_ref));
135        assert_eq!(by_ref.read_at(0, &mut buf), Ok(3));
136        assert_eq!(&buf, b"xyz");
137        assert!(Source::is_empty(&Vec::new()));
138    }
139
140    #[test]
141    fn error_displays_message() {
142        let err = SourceError::new("disk on fire");
143        assert_eq!(err.message(), "disk on fire");
144        assert_eq!(err.to_string(), "source read failed: disk on fire");
145    }
146}