subspace_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 winapi::um::winbase::FILE_FLAG_RANDOM_ACCESS
40 | winapi::um::winbase::FILE_FLAG_WRITE_THROUGH,
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(winapi::um::winbase::FILE_FLAG_SEQUENTIAL_SCAN)
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 winapi::um::winbase::FILE_FLAG_WRITE_THROUGH
61 | winapi::um::winbase::FILE_FLAG_NO_BUFFERING,
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) };
118 if err != 0 {
119 Err(std::io::Error::from_raw_os_error(err))
120 } else {
121 Ok(())
122 }
123 }
124
125 #[cfg(target_os = "macos")]
126 fn advise_random_access(&self) -> Result<()> {
127 use std::os::unix::io::AsRawFd;
128 if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_RDAHEAD, 0) } != 0 {
129 Err(std::io::Error::last_os_error())
130 } else {
131 Ok(())
132 }
133 }
134
135 #[cfg(windows)]
136 fn advise_random_access(&self) -> Result<()> {
137 Ok(())
139 }
140
141 #[cfg(target_os = "linux")]
142 fn advise_sequential_access(&self) -> Result<()> {
143 use std::os::unix::io::AsRawFd;
144 let err =
145 unsafe { libc::posix_fadvise(self.as_raw_fd(), 0, 0, libc::POSIX_FADV_SEQUENTIAL) };
146 if err != 0 {
147 Err(std::io::Error::from_raw_os_error(err))
148 } else {
149 Ok(())
150 }
151 }
152
153 #[cfg(target_os = "macos")]
154 fn advise_sequential_access(&self) -> Result<()> {
155 use std::os::unix::io::AsRawFd;
156 if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_RDAHEAD, 1) } != 0 {
157 Err(std::io::Error::last_os_error())
158 } else {
159 Ok(())
160 }
161 }
162
163 #[cfg(windows)]
164 fn advise_sequential_access(&self) -> Result<()> {
165 Ok(())
167 }
168
169 #[cfg(not(target_os = "macos"))]
170 fn disable_cache(&self) -> Result<()> {
171 Ok(())
173 }
174
175 #[cfg(target_os = "macos")]
176 fn disable_cache(&self) -> Result<()> {
177 use std::os::unix::io::AsRawFd;
178 if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_NOCACHE, 1) } != 0 {
179 Err(std::io::Error::last_os_error())
180 } else {
181 Ok(())
182 }
183 }
184
185 #[cfg(unix)]
186 fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> Result<()> {
187 std::os::unix::fs::FileExt::read_exact_at(self, buf, offset)
188 }
189
190 #[cfg(unix)]
191 fn write_all_at(&self, buf: &[u8], offset: u64) -> Result<()> {
192 std::os::unix::fs::FileExt::write_all_at(self, buf, offset)
193 }
194
195 #[cfg(windows)]
196 fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> Result<()> {
197 while !buf.is_empty() {
198 match std::os::windows::fs::FileExt::seek_read(self, buf, offset) {
199 Ok(0) => {
200 break;
201 }
202 Ok(n) => {
203 buf = &mut buf[n..];
204 offset += n as u64;
205 }
206 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {
207 }
209 Err(e) => {
210 return Err(e);
211 }
212 }
213 }
214
215 if !buf.is_empty() {
216 Err(std::io::Error::new(
217 std::io::ErrorKind::UnexpectedEof,
218 "failed to fill whole buffer",
219 ))
220 } else {
221 Ok(())
222 }
223 }
224
225 #[cfg(windows)]
226 fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> Result<()> {
227 while !buf.is_empty() {
228 match std::os::windows::fs::FileExt::seek_write(self, buf, offset) {
229 Ok(0) => {
230 return Err(std::io::Error::new(
231 std::io::ErrorKind::WriteZero,
232 "failed to write whole buffer",
233 ));
234 }
235 Ok(n) => {
236 buf = &buf[n..];
237 offset += n as u64;
238 }
239 Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {
240 }
242 Err(e) => {
243 return Err(e);
244 }
245 }
246 }
247
248 Ok(())
249 }
250}