positioned_io2/
raf.rs

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