Skip to main content

rapidgzip_core/
read_at.rs

1use std::fs::File;
2use std::io;
3use std::sync::Arc;
4
5/// Thread-safe positional compressed input.
6///
7/// Calls to [`ReadAt::read_at`] can occur concurrently and must not mutate a
8/// shared stream cursor. Implementations may return short reads, but must not
9/// return `Ok(0)` before the requested offset reaches [`ReadAt::len`]. The
10/// reported length and every byte before it must remain stable for the complete
11/// decode. Violating these requirements can cause an ordinary decoding error,
12/// but cannot violate memory safety because the trait is safe.
13pub trait ReadAt: Send + Sync {
14    /// Returns the current source length in bytes.
15    fn len(&self) -> io::Result<u64>;
16
17    /// Reads at most `buffer.len()` bytes beginning at `offset`.
18    ///
19    /// An empty buffer returns zero. A non-empty buffer returns zero only when
20    /// `offset` is at or beyond the current source length.
21    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> io::Result<usize>;
22
23    /// Returns whether this source is empty.
24    fn is_empty(&self) -> io::Result<bool> {
25        self.len().map(|length| length == 0)
26    }
27}
28
29#[cfg(unix)]
30impl ReadAt for File {
31    fn len(&self) -> io::Result<u64> {
32        self.metadata().map(|metadata| metadata.len())
33    }
34
35    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> io::Result<usize> {
36        std::os::unix::fs::FileExt::read_at(self, buffer, offset)
37    }
38}
39
40#[cfg(windows)]
41impl ReadAt for File {
42    fn len(&self) -> io::Result<u64> {
43        self.metadata().map(|metadata| metadata.len())
44    }
45
46    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> io::Result<usize> {
47        std::os::windows::fs::FileExt::seek_read(self, buffer, offset)
48    }
49}
50
51impl ReadAt for [u8] {
52    fn len(&self) -> io::Result<u64> {
53        Ok(self.len() as u64)
54    }
55
56    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> io::Result<usize> {
57        let start = usize::try_from(offset).unwrap_or(usize::MAX);
58        if start >= self.len() {
59            return Ok(0);
60        }
61        let count = buffer.len().min(self.len() - start);
62        buffer[..count].copy_from_slice(&self[start..start + count]);
63        Ok(count)
64    }
65}
66
67impl ReadAt for Vec<u8> {
68    fn len(&self) -> io::Result<u64> {
69        Ok(self.as_slice().len() as u64)
70    }
71
72    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> io::Result<usize> {
73        self.as_slice().read_at(offset, buffer)
74    }
75}
76
77impl<T: ReadAt + ?Sized> ReadAt for Arc<T> {
78    fn len(&self) -> io::Result<u64> {
79        (**self).len()
80    }
81
82    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> io::Result<usize> {
83        (**self).read_at(offset, buffer)
84    }
85}
86
87impl<T: ReadAt + ?Sized> ReadAt for Box<T> {
88    fn len(&self) -> io::Result<u64> {
89        (**self).len()
90    }
91
92    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> io::Result<usize> {
93        (**self).read_at(offset, buffer)
94    }
95}