positioned_io_preview/
raf.rs

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