Skip to main content

nectar_primitives/file/
sync_read_at.rs

1//! Sync random-access read trait for parallel file splitting.
2
3use std::io;
4
5use bytes::Bytes;
6
7/// Sync data source supporting offset-based reads.
8///
9/// Enables parallel splitting by allowing concurrent reads at different offsets.
10pub trait SyncReadAt {
11    /// Read data at offset into buffer, returning bytes read.
12    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize>;
13
14    /// Total size of the data source.
15    fn len(&self) -> u64;
16
17    /// Whether the data source is empty.
18    fn is_empty(&self) -> bool {
19        self.len() == 0
20    }
21}
22
23impl SyncReadAt for [u8] {
24    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
25        let offset = offset as usize;
26        if offset >= self.len() {
27            return Ok(0);
28        }
29        let available = self.len() - offset;
30        let to_read = buf.len().min(available);
31        buf[..to_read].copy_from_slice(&self[offset..offset + to_read]);
32        Ok(to_read)
33    }
34
35    fn len(&self) -> u64 {
36        <[u8]>::len(self) as u64
37    }
38}
39
40impl SyncReadAt for Vec<u8> {
41    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
42        self.as_slice().read_at(offset, buf)
43    }
44
45    fn len(&self) -> u64 {
46        Self::len(self) as u64
47    }
48}
49
50impl SyncReadAt for Bytes {
51    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
52        self.as_ref().read_at(offset, buf)
53    }
54
55    fn len(&self) -> u64 {
56        Self::len(self) as u64
57    }
58}
59
60impl<T: SyncReadAt + ?Sized> SyncReadAt for &T {
61    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
62        (**self).read_at(offset, buf)
63    }
64
65    fn len(&self) -> u64 {
66        (**self).len()
67    }
68}
69
70#[cfg(unix)]
71impl SyncReadAt for std::fs::File {
72    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
73        use std::os::unix::fs::FileExt;
74        FileExt::read_at(self, buf, offset)
75    }
76
77    fn len(&self) -> u64 {
78        self.metadata().map(|m| m.len()).unwrap_or(0)
79    }
80}
81
82#[cfg(windows)]
83impl SyncReadAt for std::fs::File {
84    fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
85        use std::os::windows::fs::FileExt;
86        FileExt::seek_read(self, buf, offset)
87    }
88
89    fn len(&self) -> u64 {
90        self.metadata().map(|m| m.len()).unwrap_or(0)
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_read_at_slice() {
100        let data = b"hello world";
101        let mut buf = [0u8; 5];
102
103        let n = data.as_slice().read_at(0, &mut buf).unwrap();
104        assert_eq!(n, 5);
105        assert_eq!(&buf, b"hello");
106
107        let n = data.as_slice().read_at(6, &mut buf).unwrap();
108        assert_eq!(n, 5);
109        assert_eq!(&buf, b"world");
110
111        let n = data.as_slice().read_at(9, &mut buf).unwrap();
112        assert_eq!(n, 2);
113        assert_eq!(&buf[..2], b"ld");
114
115        let n = data.as_slice().read_at(100, &mut buf).unwrap();
116        assert_eq!(n, 0);
117    }
118
119    #[test]
120    fn test_read_at_vec() {
121        let data = vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
122        let mut buf = [0u8; 3];
123
124        let n = data.read_at(5, &mut buf).unwrap();
125        assert_eq!(n, 3);
126        assert_eq!(buf, [5, 6, 7]);
127
128        assert_eq!(data.len(), 10);
129    }
130
131    #[test]
132    fn test_read_at_bytes() {
133        let data = Bytes::from_static(b"test data");
134        let mut buf = [0u8; 4];
135
136        let n = data.read_at(5, &mut buf).unwrap();
137        assert_eq!(n, 4);
138        assert_eq!(&buf, b"data");
139
140        assert_eq!(SyncReadAt::len(&data), 9);
141    }
142
143    #[test]
144    fn test_read_at_ref() {
145        let data = b"reference";
146        let r: &[u8] = data;
147        let mut buf = [0u8; 3];
148
149        let n = (&r).read_at(0, &mut buf).unwrap();
150        assert_eq!(n, 3);
151        assert_eq!(&buf, b"ref");
152    }
153}