ab_farmer_components/
file_ext.rs1use std::fs::{File, OpenOptions};
4use std::io::Result;
5
6pub trait OpenOptionsExt {
8 fn advise_random_access(&mut self) -> &mut Self;
11
12 fn advise_sequential_access(&mut self) -> &mut Self;
15
16 fn use_direct_io(&mut self) -> &mut Self;
22}
23
24impl OpenOptionsExt for OpenOptions {
25 #[cfg(not(windows))]
26 fn advise_random_access(&mut self) -> &mut Self {
27 self
29 }
30
31 #[cfg(windows)]
32 fn advise_random_access(&mut self) -> &mut Self {
33 use std::os::windows::fs::OpenOptionsExt;
34 self.custom_flags(
39 windows::Win32::Storage::FileSystem::FILE_FLAG_RANDOM_ACCESS.0
40 | windows::Win32::Storage::FileSystem::FILE_FLAG_WRITE_THROUGH.0,
41 )
42 }
43
44 #[cfg(not(windows))]
45 fn advise_sequential_access(&mut self) -> &mut Self {
46 self
48 }
49
50 #[cfg(windows)]
51 fn advise_sequential_access(&mut self) -> &mut Self {
52 use std::os::windows::fs::OpenOptionsExt;
53 self.custom_flags(windows::Win32::Storage::FileSystem::FILE_FLAG_SEQUENTIAL_SCAN.0)
54 }
55
56 #[cfg(windows)]
57 fn use_direct_io(&mut self) -> &mut Self {
58 use std::os::windows::fs::OpenOptionsExt;
59 self.custom_flags(
60 windows::Win32::Storage::FileSystem::FILE_FLAG_WRITE_THROUGH.0
61 | windows::Win32::Storage::FileSystem::FILE_FLAG_NO_BUFFERING.0,
62 )
63 }
64
65 #[cfg(target_os = "linux")]
66 fn use_direct_io(&mut self) -> &mut Self {
67 use std::os::unix::fs::OpenOptionsExt;
68 self.custom_flags(libc::O_DIRECT)
69 }
70
71 #[cfg(not(any(target_os = "linux", windows)))]
72 fn use_direct_io(&mut self) -> &mut Self {
73 self
75 }
76}
77
78pub trait FileExt {
81 fn size(&self) -> Result<u64>;
83
84 fn preallocate(&self, len: u64) -> Result<()>;
86
87 fn advise_random_access(&self) -> Result<()>;
90
91 fn advise_sequential_access(&self) -> Result<()>;
94
95 fn disable_cache(&self) -> Result<()>;
97
98 fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> Result<()>;
100
101 fn write_all_at(&self, buf: &[u8], offset: u64) -> Result<()>;
103}
104
105impl FileExt for File {
106 fn size(&self) -> Result<u64> {
107 Ok(self.metadata()?.len())
108 }
109
110 fn preallocate(&self, len: u64) -> Result<()> {
111 fs2::FileExt::allocate(self, len)
112 }
113
114 #[cfg(target_os = "linux")]
115 fn advise_random_access(&self) -> Result<()> {
116 use std::os::unix::io::AsRawFd;
117 let err = unsafe { libc::posix_fadvise(self.as_raw_fd(), 0, 0, libc::POSIX_FADV_RANDOM) };
119 if err != 0 {
120 Err(std::io::Error::from_raw_os_error(err))
121 } else {
122 Ok(())
123 }
124 }
125
126 #[cfg(target_os = "macos")]
127 fn advise_random_access(&self) -> Result<()> {
128 use std::os::unix::io::AsRawFd;
129 if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_RDAHEAD, 0) } != 0 {
131 Err(std::io::Error::last_os_error())
132 } else {
133 Ok(())
134 }
135 }
136
137 #[cfg(windows)]
138 fn advise_random_access(&self) -> Result<()> {
139 Ok(())
141 }
142
143 #[cfg(target_os = "linux")]
144 fn advise_sequential_access(&self) -> Result<()> {
145 use std::os::unix::io::AsRawFd;
146 let err =
148 unsafe { libc::posix_fadvise(self.as_raw_fd(), 0, 0, libc::POSIX_FADV_SEQUENTIAL) };
149 if err != 0 {
150 Err(std::io::Error::from_raw_os_error(err))
151 } else {
152 Ok(())
153 }
154 }
155
156 #[cfg(target_os = "macos")]
157 fn advise_sequential_access(&self) -> Result<()> {
158 use std::os::unix::io::AsRawFd;
159 if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_RDAHEAD, 1) } != 0 {
161 Err(std::io::Error::last_os_error())
162 } else {
163 Ok(())
164 }
165 }
166
167 #[cfg(windows)]
168 fn advise_sequential_access(&self) -> Result<()> {
169 Ok(())
171 }
172
173 #[cfg(not(target_os = "macos"))]
174 fn disable_cache(&self) -> Result<()> {
175 Ok(())
177 }
178
179 #[cfg(target_os = "macos")]
180 fn disable_cache(&self) -> Result<()> {
181 use std::os::unix::io::AsRawFd;
182 if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_NOCACHE, 1) } != 0 {
184 Err(std::io::Error::last_os_error())
185 } else {
186 Ok(())
187 }
188 }
189
190 #[cfg(unix)]
191 fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> Result<()> {
192 std::os::unix::fs::FileExt::read_exact_at(self, buf, offset)
193 }
194
195 #[cfg(unix)]
196 fn write_all_at(&self, buf: &[u8], offset: u64) -> Result<()> {
197 std::os::unix::fs::FileExt::write_all_at(self, buf, offset)
198 }
199
200 #[cfg(windows)]
201 fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> Result<()> {
202 while !buf.is_empty() {
203 match std::os::windows::fs::FileExt::seek_read(self, buf, offset) {
204 Ok(0) => {
205 break;
206 }
207 Ok(n) => {
208 buf = &mut buf[n..];
209 offset += n as u64;
210 }
211 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {
212 }
214 Err(e) => {
215 return Err(e);
216 }
217 }
218 }
219
220 if !buf.is_empty() {
221 Err(std::io::Error::new(
222 std::io::ErrorKind::UnexpectedEof,
223 "failed to fill whole buffer",
224 ))
225 } else {
226 Ok(())
227 }
228 }
229
230 #[cfg(windows)]
231 fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> Result<()> {
232 while !buf.is_empty() {
233 match std::os::windows::fs::FileExt::seek_write(self, buf, offset) {
234 Ok(0) => {
235 return Err(std::io::Error::new(
236 std::io::ErrorKind::WriteZero,
237 "failed to write whole buffer",
238 ));
239 }
240 Ok(n) => {
241 buf = &buf[n..];
242 offset += n as u64;
243 }
244 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {
245 }
247 Err(e) => {
248 return Err(e);
249 }
250 }
251 }
252
253 Ok(())
254 }
255}