Reworked mapped:: `create_process()`, `OpTable<T,U>`, `OpTable::execute()`.

Fortune for chacha20's current commit: Future blessing − 末吉
mmap-feature
Avril 2 years ago
parent d9f4e729c2
commit ed30981296
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -11,7 +11,7 @@ use std::{
self, self,
MaybeUninit, MaybeUninit,
}, },
borrow::BorrowMut, borrow::{BorrowMut, Cow},
convert::{TryFrom, TryInto,}, convert::{TryFrom, TryInto,},
}; };
use libc::{ use libc::{
@ -166,9 +166,11 @@ fn translate_hugetlb_size(size: usize) -> mapped_file::hugetlb::HugePage
.or_else(|| sizes_kb.iter().nth(1) .or_else(|| sizes_kb.iter().nth(1)
.or_else(|| sizes_kb.first())) .or_else(|| sizes_kb.first()))
} }
const MB: usize = 1024*1024;
const GB: usize = 1024 * MB;
match size { match size {
0..(1024*1024) => mapped_file::hugetlb::HugePage::Smallest, 0..MB => mapped_file::hugetlb::HugePage::Smallest,
(1024*1024)..(1024*1024*1024) => mapped_file::hugetlb::HugePage::Selected(check_func), MB..GB => mapped_file::hugetlb::HugePage::Selected(check_func),
very_high => mapped_file::hugetlb::HugePage::Largest, very_high => mapped_file::hugetlb::HugePage::Largest,
} }
} }
@ -191,6 +193,194 @@ fn create_sized_temp_mapping(size: usize) -> io::Result<(MappedFile<MemoryFile>,
MappedFile::new(file, size, Perm::ReadWrite, Flags::Shared).map(|x| (x, false)) MappedFile::new(file, size, Perm::ReadWrite, Flags::Shared).map(|x| (x, false))
} }
type MappedMemoryFile = MappedFile<mapped_file::file::memory::MemoryFile>;
/// How a mapped operation should optimally take place
//TODO: Make optimised mapping operations for each one, like `fn process(self, enc: &mut Crypter) -> io::Result<usize>`
pub enum OpTable<T, U>
{
/// Both input and output are mapped
Both(MappedFile<T>, MappedFile<U>),
/// Input is mapped, but output is not. Work is done through a temporary map, then copied to `U`.
Input(MappedFile<T>, MappedMemoryFile, U),
/// Output is mapped, but input is not. Work is done from a temporary map
Output(T, MappedMemoryFile, MappedFile<U>),
/// Streaming mode (`read()`+`write()` only)
Neither(T, U),
}
impl<T, U> OpTable<T, U>
{
/// Consume into either a mapping line, or the in/out tuple if neither are mapped.
#[inline]
pub fn only_mapped(self) -> Result<Self, (T, U)>
{
match self {
Self::Neither(t, u) => Err((t, u)),
this => Ok(this),
}
}
}
impl<T: io::Read+AsRawFd, U: io::Write+AsRawFd> OpTable<T, U>
{
fn pre_process(&mut self) -> io::Result<()>
{
match self {
Self::Both(input, output)
=> {
let _ = input.advise(mapped_file::Advice::Sequential, Some(true));
let _ = output.advise(mapped_file::Advice::Sequential, None);
},
Self::Input(input, mem)
=> {
let _ = input.advise(mapped_file::Advice::Sequential, Some(true));
let _ = mem.advise(mapped_file::Advice::Sequential, None);
},
Self::Output(input, mem, _)
=> {
let _ = mem.advise(mapped_file::Advice::Sequential, Some(true));
std::io::copy(&mut input, &mut mem)?;
},
_ => (),
}
Ok(())
}
fn post_process(&mut self) -> io::Result<()>
{
match self {
Self::Both(_, output) => drop(output.flush(mapped_file::Flush::Wait)?),
Self::Input(_, mut mem, mut output) => drop(std::io::copy(&mut mem, &mut output)?),
Self::Output(_, _, mut output) => drop(output.flush(mapped_file::Flush::Wait)?),
Self::Neither(_, stream) => stream.flush()?,
_ => (),
}
Ok(())
}
/// Execute this en/decryption in an optimised function
pub fn execute(mut self, mut mode: impl BorrowMut<Crypter>) -> io::Result<usize>
{
self.pre_process()?;
let mode: &mut Crypter = mode.borrow_mut();
match self {
Self::Both(mut input, mut output) => {
let len = std::cmp::min(input.len(), output.len());
process_mapped_files(mode, &mut input, &mut output)?;
self.post_process()?;
Ok(len)
},
Self::Input(mut input, mut output, _) => {
let len = std::cmp::min(input.len(), output.len());
process_mapped_files(mode, &mut input, &mut output)?;
self.post_process()?;
Ok(len)
},
Self::Output(_, mut input, mut output) => {
let len = std::cmp::min(input.len(), output.len());
process_mapped_files(mode, &mut input, &mut output)?;
self.post_process()?;
Ok(len)
},
Self::Neither(sin, sout) => {
const BUFFER_SIZE: usize = 1024*1024;
macro_rules! try_allocmem {
($size:expr) => {
{
let size = usize::from($size);
let hsz = mapped_file::hugetlb::HugePage::Dynamic { kilobytes: size/1024 }; // 1MB buffer
hsz.compute_huge()
.and_then(|huge| mapped_file::file::memory::MemoryFile::with_size_hugetlb(size, huge).ok())
.or_else(|| mapped_file::file::memory::MemoryFile::with_size(size).ok())
.and_then(|file| MappedFile::new(file, size, Perm::ReadWrite, Flags::Private).ok())
}
};
($mem:expr, $size:expr) => {
$mem.map(|x| Cow::Borrowed(&mut x[..])).unwrap_or_else(|| Cow::Owned(vec![0u8; $size]));
}
}
let mut _mem = try_allocmem!(BUFFER_SIZE);
let mut _memo = try_allocmem!(BUFFER_SIZE);
let mut buffer = try_allocmem!(_mem, BUFFER_SIZE);
let mut buffero = try_allocmem!(_memo, BUFFER_SIZE);
let mut read =0;
let mut cur;
while { cur = sin.read(&mut buffer[..])?; cur > 0 } {
/*let cur =*/ mode.update(&buffer[..cur], &mut buffero[..cur])?;
sout.write_all(&buffero[..cur])?;
read += cur;
}
self.post_process()?;
Ok(read)
},
}
}
}
#[inline]
fn sized_then_or<T: AsRawFd, U, F>(stream: T, trans: F) -> Result<U, T>
where F: FnOnce(T, usize) -> U
{
let Some(size): usize = raw_file_size(&stream).and_then(u64::into).ok() else { return Err(stream); };
Some(trans(stream, trans))
}
#[inline]
fn map_size_or<T: AsRawFd, U, F>(stream: T, size: usize, trans: F) -> Result<U, T>
where F: FnOnce(MappedFile<T>, usize) -> U
{
if try_map_to(&stream, size) {
// Sized
match MappedFile::try_new(stream, size, Perm::ReadWrite, Flags::Shared) {
Ok(map) => Ok(trans(map, size)),
Err(e) => Err(e.into_inner()),
}
} else {
// Unsized
Err(stream)
}
}
/// Create an optimised call table for the cryptographic transformation from `from` to `to`.
pub fn create_process<T: AsRawFd, U: AsRawFd>(from: T, to: U) -> OpTable<T, U>
{
let (input, buffsz) = match sized_then_or(from, |input, input_size| {
(match MappedFile::try_new(input, input_size, Perm::Readonly, Flags::Private) {
Ok(m) => Ok(m),
Err(e) => Err(e.into_inner()),
}, input_size)
}) {
Ok((i, bs)) => (i, Some(bs)),
Err(e) => (Err(e), None),
};
let (output, outsz) = {
if let Some(buffsz) = buffsz.or_else(|| raw_file_size(&to).ok()) {
match map_size_or(to, buffsz, |mmap, size| {
(mmap, size)
}) {
Ok((m, s)) => (Ok(m), Some(s)),
Err(e) => (Err(e), if buffsz == 0 { None } else { Some(buffsz) }),
}
} else {
Err((to, None))
}
};
match ((input, buffsz), (output, outsz)) {
// Check for all combinations of mapping successes or failures
((Ok(min), isz), (Ok(mout), osz)) => OpTable::Both(min, mout),
((Ok(min), isz), (Err(sout), osz)) => OpTable::Input(min, create_sized_temp_mapping(isz.or(osz)), sout),
((Err(sin), isz), (Ok(mout), osz)) => OpTable::Output(sin, create_sized_temp_mapping(osz.or(isz)), mout),
((Err(sin, isz), (Err(sout), osz))) => OpTable::Neither(sin, sout),
}
}
#[cfg(feature="try_process-old")]
const _:() = {
pub fn try_process(mut mode: impl BorrowMut<Crypter>) -> io::Result<io::Result<()>> pub fn try_process(mut mode: impl BorrowMut<Crypter>) -> io::Result<io::Result<()>>
{ {
let mode = mode.borrow_mut(); let mode = mode.borrow_mut();
@ -286,3 +476,4 @@ pub fn try_process(mut mode: impl BorrowMut<Crypter>) -> io::Result<io::Result<(
//todo!("Try to map the stdin and stdout streams, if that fails, return Err(last_os_err)."); //todo!("Try to map the stdin and stdout streams, if that fails, return Err(last_os_err).");
//todo!("return Ok(process_mapped_files(mode, mstdin, mstdout, key, iv))") //todo!("return Ok(process_mapped_files(mode, mstdin, mstdout, key, iv))")
} }
};

Loading…
Cancel
Save