subspace_farmer_components/
file_ext.rsuse std::fs::{File, OpenOptions};
use std::io::Result;
pub trait OpenOptionsExt {
fn advise_random_access(&mut self) -> &mut Self;
fn advise_sequential_access(&mut self) -> &mut Self;
fn use_direct_io(&mut self) -> &mut Self;
}
impl OpenOptionsExt for OpenOptions {
#[cfg(not(windows))]
fn advise_random_access(&mut self) -> &mut Self {
self
}
#[cfg(windows)]
fn advise_random_access(&mut self) -> &mut Self {
use std::os::windows::fs::OpenOptionsExt;
self.custom_flags(
winapi::um::winbase::FILE_FLAG_RANDOM_ACCESS
| winapi::um::winbase::FILE_FLAG_WRITE_THROUGH,
)
}
#[cfg(not(windows))]
fn advise_sequential_access(&mut self) -> &mut Self {
self
}
#[cfg(windows)]
fn advise_sequential_access(&mut self) -> &mut Self {
use std::os::windows::fs::OpenOptionsExt;
self.custom_flags(winapi::um::winbase::FILE_FLAG_SEQUENTIAL_SCAN)
}
#[cfg(windows)]
fn use_direct_io(&mut self) -> &mut Self {
use std::os::windows::fs::OpenOptionsExt;
self.custom_flags(
winapi::um::winbase::FILE_FLAG_WRITE_THROUGH
| winapi::um::winbase::FILE_FLAG_NO_BUFFERING,
)
}
#[cfg(target_os = "linux")]
fn use_direct_io(&mut self) -> &mut Self {
use std::os::unix::fs::OpenOptionsExt;
self.custom_flags(libc::O_DIRECT)
}
#[cfg(not(any(target_os = "linux", windows)))]
fn use_direct_io(&mut self) -> &mut Self {
self
}
}
pub trait FileExt {
fn size(&self) -> Result<u64>;
fn preallocate(&self, len: u64) -> Result<()>;
fn advise_random_access(&self) -> Result<()>;
fn advise_sequential_access(&self) -> Result<()>;
fn disable_cache(&self) -> Result<()>;
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> Result<()>;
fn write_all_at(&self, buf: &[u8], offset: u64) -> Result<()>;
}
impl FileExt for File {
fn size(&self) -> Result<u64> {
Ok(self.metadata()?.len())
}
fn preallocate(&self, len: u64) -> Result<()> {
fs2::FileExt::allocate(self, len)
}
#[cfg(target_os = "linux")]
fn advise_random_access(&self) -> Result<()> {
use std::os::unix::io::AsRawFd;
let err = unsafe { libc::posix_fadvise(self.as_raw_fd(), 0, 0, libc::POSIX_FADV_RANDOM) };
if err != 0 {
Err(std::io::Error::from_raw_os_error(err))
} else {
Ok(())
}
}
#[cfg(target_os = "macos")]
fn advise_random_access(&self) -> Result<()> {
use std::os::unix::io::AsRawFd;
if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_RDAHEAD, 0) } != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(windows)]
fn advise_random_access(&self) -> Result<()> {
Ok(())
}
#[cfg(target_os = "linux")]
fn advise_sequential_access(&self) -> Result<()> {
use std::os::unix::io::AsRawFd;
let err =
unsafe { libc::posix_fadvise(self.as_raw_fd(), 0, 0, libc::POSIX_FADV_SEQUENTIAL) };
if err != 0 {
Err(std::io::Error::from_raw_os_error(err))
} else {
Ok(())
}
}
#[cfg(target_os = "macos")]
fn advise_sequential_access(&self) -> Result<()> {
use std::os::unix::io::AsRawFd;
if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_RDAHEAD, 1) } != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(windows)]
fn advise_sequential_access(&self) -> Result<()> {
Ok(())
}
#[cfg(not(target_os = "macos"))]
fn disable_cache(&self) -> Result<()> {
Ok(())
}
#[cfg(target_os = "macos")]
fn disable_cache(&self) -> Result<()> {
use std::os::unix::io::AsRawFd;
if unsafe { libc::fcntl(self.as_raw_fd(), libc::F_NOCACHE, 1) } != 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(unix)]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> Result<()> {
std::os::unix::fs::FileExt::read_exact_at(self, buf, offset)
}
#[cfg(unix)]
fn write_all_at(&self, buf: &[u8], offset: u64) -> Result<()> {
std::os::unix::fs::FileExt::write_all_at(self, buf, offset)
}
#[cfg(windows)]
fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> Result<()> {
while !buf.is_empty() {
match std::os::windows::fs::FileExt::seek_read(self, buf, offset) {
Ok(0) => {
break;
}
Ok(n) => {
buf = &mut buf[n..];
offset += n as u64;
}
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {
}
Err(e) => {
return Err(e);
}
}
}
if !buf.is_empty() {
Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
} else {
Ok(())
}
}
#[cfg(windows)]
fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> Result<()> {
while !buf.is_empty() {
match std::os::windows::fs::FileExt::seek_write(self, buf, offset) {
Ok(0) => {
return Err(std::io::Error::new(
std::io::ErrorKind::WriteZero,
"failed to write whole buffer",
));
}
Ok(n) => {
buf = &buf[n..];
offset += n as u64;
}
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {
}
Err(e) => {
return Err(e);
}
}
}
Ok(())
}
}