5bca12406b
algo.
155 lines
3.9 KiB
Rust
155 lines
3.9 KiB
Rust
use std::fs::{File, OpenOptions};
|
|
use std::io::{self, Seek, SeekFrom};
|
|
use std::ops::Index;
|
|
use std::path::Path;
|
|
|
|
use crate::cli::CONFIG;
|
|
|
|
use anyhow::{Context, anyhow};
|
|
|
|
/// Get length of data stream.
|
|
/// Physical length of data stream in bytes
|
|
/// (multiple of sector_size, rather than actual).
|
|
///
|
|
/// This will attempt to return the stream to its current read position.
|
|
pub fn get_stream_length<S: Seek>(stream: &mut S) -> io::Result<u64> {
|
|
let pos = stream.stream_position()?;
|
|
let len = stream.seek(SeekFrom::End(0));
|
|
|
|
stream.seek(SeekFrom::Start(pos))?;
|
|
|
|
len
|
|
}
|
|
|
|
#[cfg(all(unix, not(target_os = "macos")))]
|
|
pub fn load_input() -> anyhow::Result<File> {
|
|
use std::os::unix::fs::OpenOptionsExt;
|
|
|
|
let mut options = OpenOptions::new();
|
|
options.read(true);
|
|
|
|
if CONFIG.direct_io {
|
|
options.custom_flags(libc::O_DIRECT);
|
|
}
|
|
|
|
options
|
|
.open(&CONFIG.input)
|
|
.with_context(|| format!("Failed to open input file: {}", &CONFIG.input.display()))
|
|
}
|
|
|
|
#[cfg(any(not(unix), target_os = "macos"))]
|
|
pub fn load_input() -> anyhow::Result<File> {
|
|
OpenOptions::new()
|
|
.read(true)
|
|
.open(&CONFIG.input)
|
|
.with_context(|| format!("Failed to open input file: {}", &CONFIG.input.display()))
|
|
}
|
|
|
|
pub fn load_output() -> anyhow::Result<File> {
|
|
OpenOptions::new()
|
|
.read(true)
|
|
.write(true)
|
|
.create(true)
|
|
.open(crate::path::OUTPUT_PATH.clone())
|
|
.with_context(|| {
|
|
format!(
|
|
"Failed to open/create output file at: {}",
|
|
crate::path::OUTPUT_PATH.display()
|
|
)
|
|
})
|
|
}
|
|
|
|
pub fn load_map_read() -> std::io::Result<File> {
|
|
OpenOptions::new()
|
|
.read(true)
|
|
.open(crate::path::MAP_PATH.clone())
|
|
}
|
|
|
|
pub fn load_map_write() -> anyhow::Result<File> {
|
|
// Attempt to check if a map exists on the disk.
|
|
// If so, make a backup of it.
|
|
//
|
|
// This should be recoverable by just skipping over this error and logging a warning,
|
|
// but for now it will be an error condition.
|
|
if std::fs::exists(crate::path::MAP_PATH.clone())
|
|
.context("Could not check if map exists in fs to make a backup.")?
|
|
{
|
|
backup(crate::path::MAP_PATH.clone())?;
|
|
}
|
|
|
|
OpenOptions::new()
|
|
.write(true)
|
|
.create(true)
|
|
.truncate(true) // Wipe old map, in case we skip over backing up the old one.
|
|
.open(crate::path::MAP_PATH.clone())
|
|
.with_context(|| {
|
|
format!(
|
|
"Failed to open map file at: {}",
|
|
crate::path::MAP_PATH.display()
|
|
)
|
|
})
|
|
}
|
|
|
|
fn backup<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
|
|
std::fs::rename(
|
|
&path,
|
|
format!("{}.bak", path.as_ref().to_path_buf().display()),
|
|
)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[repr(C, align(512))]
|
|
pub struct DirectIOBuffer(pub [u8; crate::MAX_BUFFER_SIZE]);
|
|
|
|
impl DirectIOBuffer {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
}
|
|
|
|
impl Default for DirectIOBuffer {
|
|
fn default() -> Self {
|
|
Self([crate::FB_NULL_VALUE; _])
|
|
}
|
|
}
|
|
|
|
impl From<[u8; crate::MAX_BUFFER_SIZE]> for DirectIOBuffer {
|
|
fn from(value: [u8; crate::MAX_BUFFER_SIZE]) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&[u8]> for DirectIOBuffer {
|
|
type Error = anyhow::Error;
|
|
|
|
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
|
if value.len() > crate::MAX_BUFFER_SIZE {
|
|
return Err(anyhow!("Provided slice is larger than MAX_BUFFER_SIZE."));
|
|
}
|
|
|
|
Ok(Self(value.try_into()?))
|
|
}
|
|
}
|
|
|
|
impl AsRef<[u8]> for DirectIOBuffer {
|
|
fn as_ref(&self) -> &[u8] {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl AsMut<[u8]> for DirectIOBuffer {
|
|
fn as_mut(&mut self) -> &mut [u8] {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl<Idx> Index<Idx> for DirectIOBuffer
|
|
where
|
|
Idx: std::slice::SliceIndex<[u8], Output = [u8]>,
|
|
{
|
|
type Output = Idx::Output;
|
|
fn index(&self, index: Idx) -> &Self::Output {
|
|
&self.0.as_slice()[index]
|
|
}
|
|
}
|