diff --git a/src/ext.rs b/src/ext.rs index cd9a0f9..582eb0e 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -11,6 +11,11 @@ use std::{ #[derive(Debug, Clone)] pub struct HexStringIter(I, [u8; 2]); +/// Represents any value that can be sent and shared across threads +pub type Dynamic = dyn std::any::Any + Send + Sync + 'static; +/// Represents any thread-local value +pub type LocalDynamic = dyn std::any::Any + 'static; + impl> HexStringIter { /// Write this hex string iterator to a formattable buffer diff --git a/src/mapped.rs b/src/mapped.rs index cc39620..69a9f63 100644 --- a/src/mapped.rs +++ b/src/mapped.rs @@ -13,6 +13,7 @@ use std::{ }, borrow::{BorrowMut, Cow}, convert::{TryFrom, TryInto,}, + fmt, error, }; use libc::{ mmap, munmap, madvise, MAP_FAILED, @@ -62,7 +63,7 @@ pub fn raw_file_size(fd: &(impl AsRawFd + ?Sized)) -> io::Result #[inline] fn try_truncate(fd: &(impl AsRawFd + ?Sized), to: u64) -> io::Result<()> { - //use libc::ftruncate; + use libc::ftruncate; let to = to.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; match unsafe { ftruncate(fd.as_raw_fd(), to) } { 0 => Ok(()), @@ -152,7 +153,7 @@ fn try_map_to(file: &T, file_size: usize) -> bool } // Grow file unsafe { - libc::ftruncate(file.as_raw_fd(), file_size) == 0 + libc::ftruncate(file.as_raw_fd(), if file_size > i64::MAX as u64 { i64::MAX } else { file_size as i64 }) == 0 } } @@ -169,30 +170,53 @@ fn translate_hugetlb_size(size: usize) -> mapped_file::hugetlb::HugePage const MB: usize = 1024*1024; const GB: usize = 1024 * MB; match size { - 0..MB => mapped_file::hugetlb::HugePage::Smallest, - MB..GB => 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, } } /// Create and map a temporary memory file of this size. /// -/// This function may optionally choose to huge hugepages if `size` is large enough +/// # Hugetlb +/// This function may optionally choose to huge hugepages if `size` is large enough and `HUGE` is set to true. +/// If you want to read/write from this file too, call `create_sized_temp_mapping_at::()` instead, or `create_sized_basic_mapping()`. +#[inline] fn create_sized_temp_mapping(size: usize) -> io::Result<(MappedFile, bool)> +{ + create_sized_temp_mapping_at::(size) +} +/// Create and map a temporary memory file of this size. +/// +/// # Hugetlb +/// This function may optionally choose to huge hugepages if `size` is large enough and `HUGE` is set to true. +/// If you want to read/write from this file too, call with `HUGE = false`. +fn create_sized_temp_mapping_at(size: usize) -> io::Result<(MappedFile, bool)> { const HUGETLB_THRESH: usize = 1024 * 1024; // 1MB let file = match size { - 0 => MemoryFile::new(), - 0..HUGETLB_THRESH => MemoryFile::with_size(size), - size => { + 0..=HUGETLB_THRESH => MemoryFile::with_size(size), + size if HUGE => { let hugetlb = translate_hugetlb_size(size); - let file = MemoryFile::with_size_hugetlb(size, hugetlb)?; + let file = if let Some(flag) =hugetlb.compute_huge() { + MemoryFile::with_size_hugetlb(size, flag)? + } else { + MemoryFile::with_size(size)? + }; return MappedFile::new(file, size, Perm::ReadWrite, Flags::Shared.with_hugetlb(hugetlb)).map(|x| (x, true)); - }, + }, + 0 | + _ => MemoryFile::new(), }?; MappedFile::new(file, size, Perm::ReadWrite, Flags::Shared).map(|x| (x, false)) } +#[inline(always)] +fn create_sized_basic_mapping(size: usize) -> io::Result> +{ + create_sized_temp_mapping(size).map(|(x, _)| x) +} + type MappedMemoryFile = MappedFile; /// How a mapped operation should optimally take place @@ -232,7 +256,7 @@ impl OpTable let _ = input.advise(mapped_file::Advice::Sequential, Some(true)); let _ = output.advise(mapped_file::Advice::Sequential, None); }, - Self::Input(input, mem) + Self::Input(input, mem, _) => { let _ = input.advise(mapped_file::Advice::Sequential, Some(true)); let _ = mem.advise(mapped_file::Advice::Sequential, None); @@ -240,7 +264,7 @@ impl OpTable Self::Output(input, mem, _) => { let _ = mem.advise(mapped_file::Advice::Sequential, Some(true)); - std::io::copy(&mut input, &mut mem)?; + std::io::copy(&mut input, &mut &mut mem[..])?; //TODO: When mapped_file is updated to add `inner_mut()`, use that instead of the mapped array as destination (gives access to splice et all.) }, _ => (), } @@ -250,7 +274,7 @@ impl OpTable { 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::Input(_, mut mem, mut output) => drop(std::io::copy(&mut &mem[..], &mut output)?), //TODO: When mapped_file is updated to add `inner_mut()`, use that instead of the mapped array as source (gives access to splice et all.) Self::Output(_, _, mut output) => drop(output.flush(mapped_file::Flush::Wait)?), Self::Neither(_, stream) => stream.flush()?, _ => (), @@ -298,7 +322,7 @@ impl OpTable } }; ($mem:expr, $size:expr) => { - $mem.map(|x| Cow::Borrowed(&mut x[..])).unwrap_or_else(|| Cow::Owned(vec![0u8; $size])); + $mem.map(|x| Cow::Borrowed(&mut x[..])).unwrap_or_else(|| Cow::Owned(vec![0u8; $size])) } } let mut _mem = try_allocmem!(BUFFER_SIZE); @@ -324,8 +348,8 @@ impl OpTable 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)) + let Some(size) = raw_file_size(&stream).ok().and_then(|x| u64::try_into(x).ok()) else { return Err(stream); }; + Ok(trans(stream, size)) } #[inline] @@ -344,8 +368,69 @@ fn map_size_or(stream: T, size: usize, trans: F) -> Result) -> fmt::Result + { + match self { + Self::IO(io) => write!(f, "io error: {}", io), + _ => f.write_str("unknown"), + } + } +} + +pub struct ProcessError { + kind: ProcessErrorKind, + context: Option>, +} + +impl fmt::Debug for ProcessError +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + f.debug_struct("ProcessError") + .field("kind", &self.kind) + .finish_non_exhaustive() + } +} +impl error::Error for ProcessError{} +impl fmt::Display for ProcessError +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + f.write_str("fatal processing error: ")?; + self.kind.fmt(f) + } +} + + +impl From for ProcessError +{ + #[inline] + fn from(from: io::Error) -> Self + { + Self { + kind: ProcessErrorKind::IO(from), + context: None, + } + } +} + + + /// Create an optimised call table for the cryptographic transformation from `from` to `to`. -pub fn create_process(from: T, to: U) -> OpTable +pub fn try_create_process(from: T, to: U) -> Result, ProcessError> { let (input, buffsz) = match sized_then_or(from, |input, input_size| { (match MappedFile::try_new(input, input_size, Perm::Readonly, Flags::Private) { @@ -358,7 +443,7 @@ pub fn create_process(from: T, to: U) -> OpTable }; let (output, outsz) = { - if let Some(buffsz) = buffsz.or_else(|| raw_file_size(&to).ok()) { + if let Some(buffsz) = buffsz.or_else(|| raw_file_size(&to).ok().and_then(|x| usize::try_from(x).ok())) { match map_size_or(to, buffsz, |mmap, size| { (mmap, size) }) { @@ -366,17 +451,22 @@ pub fn create_process(from: T, to: U) -> OpTable Err(e) => (Err(e), if buffsz == 0 { None } else { Some(buffsz) }), } } else { - Err((to, None)) + (Err(to), None) } }; - match ((input, buffsz), (output, outsz)) { + Ok(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), - } + ((Ok(min), isz), (Err(sout), osz)) => OpTable::Input(min, create_sized_temp_mapping(isz.or(osz).unwrap_or(0))?.0, sout), + ((Err(sin), isz), (Ok(mout), osz)) => OpTable::Output(sin, create_sized_basic_mapping(osz.or(isz).unwrap_or(0))?, mout), + ((Err(sin), isz), (Err(sout), osz)) => OpTable::Neither(sin, sout), + }) +} + +pub fn try_process(_mode: impl BorrowMut) -> io::Result> +{ + todo!("use `try_create_process()`") } #[cfg(feature="try_process-old")]