diff --git a/src/mapped.rs b/src/mapped.rs index b664d14..cc39620 100644 --- a/src/mapped.rs +++ b/src/mapped.rs @@ -11,7 +11,7 @@ use std::{ self, MaybeUninit, }, - borrow::BorrowMut, + borrow::{BorrowMut, Cow}, convert::{TryFrom, TryInto,}, }; 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.first())) } + const MB: usize = 1024*1024; + const GB: usize = 1024 * MB; match size { - 0..(1024*1024) => mapped_file::hugetlb::HugePage::Smallest, - (1024*1024)..(1024*1024*1024) => mapped_file::hugetlb::HugePage::Selected(check_func), + 0..MB => mapped_file::hugetlb::HugePage::Smallest, + MB..GB => mapped_file::hugetlb::HugePage::Selected(check_func), very_high => mapped_file::hugetlb::HugePage::Largest, } } @@ -191,6 +193,194 @@ fn create_sized_temp_mapping(size: usize) -> io::Result<(MappedFile, MappedFile::new(file, size, Perm::ReadWrite, Flags::Shared).map(|x| (x, false)) } +type MappedMemoryFile = MappedFile; + +/// 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` +pub enum OpTable +{ + /// Both input and output are mapped + Both(MappedFile, MappedFile), + /// Input is mapped, but output is not. Work is done through a temporary map, then copied to `U`. + Input(MappedFile, MappedMemoryFile, U), + /// Output is mapped, but input is not. Work is done from a temporary map + Output(T, MappedMemoryFile, MappedFile), + /// Streaming mode (`read()`+`write()` only) + Neither(T, U), +} + +impl OpTable +{ + /// Consume into either a mapping line, or the in/out tuple if neither are mapped. + #[inline] + pub fn only_mapped(self) -> Result + { + match self { + Self::Neither(t, u) => Err((t, u)), + this => Ok(this), + } + } +} + +impl OpTable +{ + 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) -> io::Result + { + 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(stream: T, trans: F) -> Result + 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(stream: T, size: usize, trans: F) -> Result + where F: FnOnce(MappedFile, 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(from: T, to: U) -> OpTable +{ + 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) -> io::Result> { let mode = mode.borrow_mut(); @@ -286,3 +476,4 @@ pub fn try_process(mut mode: impl BorrowMut) -> io::Result