positioned_io/
raf.rs

1#[cfg(windows)]
2use std::io::{Seek, SeekFrom};
3#[cfg(unix)]
4use std::os::unix::fs::FileExt;
5#[cfg(windows)]
6use std::os::windows::fs::FileExt;
7use std::{fs::File, io, io::Write, path::Path};
8
9use super::{ReadAt, Size, WriteAt};
10
11/// A wrapper for `File` that provides optimized random access through
12/// `ReadAt` and `WriteAt`.
13///
14/// * On Unix the operating system is advised that reads will be in random
15///   order (`FADV_RANDOM`).
16/// * On Windows the implementation is orders of magnitude faster than `ReadAt`
17///   directly on `File`.
18///
19/// # Examples
20///
21/// Read the fifth 512-byte sector of a file:
22///
23/// ```
24/// # use std::error::Error;
25/// #
26/// # fn try_main() -> Result<(), Box<Error>> {
27/// use positioned_io::{RandomAccessFile, ReadAt};
28///
29/// // open a file (note: binding does not need to be mut)
30/// let raf = RandomAccessFile::open("tests/pi.txt")?;
31///
32/// // read up to 512 bytes
33/// let mut buf = [0; 512];
34/// let bytes_read = raf.read_at(2048, &mut buf)?;
35/// #     assert!(buf.starts_with(b"4"));
36/// #     Ok(())
37/// # }
38/// #
39/// # fn main() {
40/// #     try_main().unwrap();
41/// # }
42#[derive(Debug)]
43pub struct RandomAccessFile {
44    file: File,
45    #[cfg(not(unix))]
46    pos: u64,
47}
48
49impl RandomAccessFile {
50    /// [Opens](https://doc.rust-lang.org/std/fs/struct.File.html#method.open)
51    /// a file for random access.
52    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<RandomAccessFile> {
53        RandomAccessFile::try_new(File::open(path)?)
54    }
55
56    /// Creates a `RandomAccessFile` wrapper around a `File`.
57    pub fn try_new(file: File) -> io::Result<RandomAccessFile> {
58        RandomAccessFile::try_new_impl(file)
59    }
60
61    #[cfg(all(unix, target_os = "linux"))]
62    fn try_new_impl(file: File) -> io::Result<RandomAccessFile> {
63        unsafe {
64            use std::os::unix::io::AsRawFd;
65            libc::posix_fadvise(file.as_raw_fd(), 0, 0, libc::POSIX_FADV_RANDOM);
66        }
67
68        Ok(RandomAccessFile { file })
69    }
70
71    #[cfg(all(unix, not(target_os = "linux")))]
72    fn try_new_impl(file: File) -> io::Result<RandomAccessFile> {
73        Ok(RandomAccessFile { file })
74    }
75
76    #[cfg(not(unix))]
77    fn try_new_impl(mut file: File) -> io::Result<RandomAccessFile> {
78        let pos = file.seek(SeekFrom::Current(0))?;
79        Ok(RandomAccessFile { file, pos })
80    }
81
82    /// Tries to unwrap the inner `File`.
83    pub fn try_into_inner(self) -> Result<File, (RandomAccessFile, io::Error)> {
84        RandomAccessFile::try_into_inner_impl(self)
85    }
86
87    #[cfg(unix)]
88    fn try_into_inner_impl(self) -> Result<File, (RandomAccessFile, io::Error)> {
89        Ok(self.file)
90    }
91
92    #[cfg(not(unix))]
93    fn try_into_inner_impl(mut self) -> Result<File, (RandomAccessFile, io::Error)> {
94        match self.file.seek(SeekFrom::Start(self.pos)) {
95            Ok(_) => Ok(self.file),
96            Err(err) => Err((self, err)),
97        }
98    }
99}
100
101#[cfg(unix)]
102impl ReadAt for RandomAccessFile {
103    #[inline]
104    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
105        FileExt::read_at(&self.file, buf, pos)
106    }
107}
108
109#[cfg(unix)]
110impl WriteAt for &RandomAccessFile {
111    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
112        FileExt::write_at(&self.file, buf, pos)
113    }
114
115    fn flush(&mut self) -> io::Result<()> {
116        Write::flush(&mut &self.file)
117    }
118}
119
120#[cfg(windows)]
121impl ReadAt for RandomAccessFile {
122    #[inline]
123    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
124        FileExt::seek_read(&self.file, buf, pos)
125    }
126}
127
128#[cfg(windows)]
129impl WriteAt for &RandomAccessFile {
130    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
131        FileExt::seek_write(&self.file, buf, pos)
132    }
133
134    fn flush(&mut self) -> io::Result<()> {
135        Write::flush(&mut &self.file)
136    }
137}
138
139impl WriteAt for RandomAccessFile {
140    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
141        WriteAt::write_at(&mut &*self, pos, buf)
142    }
143
144    fn flush(&mut self) -> io::Result<()> {
145        WriteAt::flush(&mut &*self)
146    }
147}
148
149impl Size for RandomAccessFile {
150    fn size(&self) -> io::Result<Option<u64>> {
151        self.file.size()
152    }
153}