diff --git a/Cargo.toml b/Cargo.toml index 0c801bd..a3d70c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "mapped-file" description = "Construct a memory mapping over any file object" keywords = ["unix", "mmap", "generic", "file", "fd"] -version = "0.0.7" +version = "0.0.8" edition = "2021" repository="https://github.com/notflan/mapped-file" license="MIT" diff --git a/src/file.rs b/src/file.rs index 27aacb1..72505c5 100644 --- a/src/file.rs +++ b/src/file.rs @@ -21,6 +21,74 @@ pub use self::{ pub mod memory; +#[derive(Debug)] +enum MaybeMappedInner +{ + Raw(T), + Copied(memory::MemoryFile), +} + +impl MaybeMappedInner +{ + pub fn from_stat(mut file: T) -> io::Result<(Self, u64)> + { + use libc::fstat; + let fd = file.as_raw_fd(); + let sz = unsafe { + let mut stat = std::mem::MaybeUninit::uninit(); + if fstat(fd, stat.as_mut_ptr()) != 0 { + let mut mem = memory::MemoryFile::new()?; + let count = std::io::copy(&mut file, &mut mem)?; + return Ok((Self::Copied(mem), count)); + } + stat.assume_init().st_size & i64::MAX + } as u64; + Ok((Self::Raw(file), sz)) + } +} + +impl MaybeMappedInner +{ + #[inline] + pub unsafe fn into_file(self) -> std::fs::File + { + let fd = match self { + Self::Raw(r) => r.into_raw_fd(), + Self::Copied(c) => c.into_raw_fd(), + }; + + FromRawFd::from_raw_fd(fd) + } +} + +impl AsRawFd for MaybeMappedInner +where T: AsRawFd +{ + #[inline] + fn as_raw_fd(&self) -> RawFd { + match self { + Self::Copied(c) => c.as_raw_fd(), + Self::Raw(r) => r.as_raw_fd(), + } + } +} + +/// Attempt to map a file, if it fails, copy that file into memory and map that. +/// +/// # Returns +/// A map over the file, or a map over an in-memory copy of the file. +pub fn try_map_or_cloned(file: F, perm: Perm, flags: impl MapFlags) -> io::Result> +{ + let (len, file) = { + let (file, size) = MaybeMappedInner::from_stat(file)?; + let size = usize::try_from(size).map_err(|_| io::Error::new(io::ErrorKind::Unsupported, "File size exceeds pointer word width"))?; + (size, unsafe { + file.into_file() + }) + }; + MappedFile::new(file, len, perm, flags) +} + #[cfg(test)] mod tests {