diff --git a/Makefile b/Makefile index a5c2101..6c29a0b 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ OPT_FLAGS_RUST?= -C target-cpu=native OPT_FLAGS?= -march=native -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \ -floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block +FEAT_RUST?= threads FEAT_CFLAGS?= -D_RUN_THREADED=0 FEAT_LDFLAGS?= -lpthread @@ -40,6 +41,9 @@ debug: | dirs $(PROJECT)-debug .PHONY: pgo pgo: | dirs $(PROJECT)-pgo +.PHONY: rs +rs: | $(PROJECT)-rs + dirs: @mkdir -p {obj,prof}/src @@ -60,6 +64,11 @@ $(PROJECT)-debug: LDFLAGS := $(DEBUG_LDFLAGS) $(LDFLAGS) $(PROJECT)-debug: $(OBJ) $(CC) $^ $(CFLAGS) -o $@ $(LDFLAGS) + +$(PROJECT)-rs: + cd fcmprs && OPT_FLAGS="$(OPT_FLAGS_RUST)" CARGO_FEATURES="$(FEAT_RUST)" $(MAKE) + cp -f ./fcmprs/target/release/fcmprs $@ + pgo-generate: CFLAGS := $(RELEASE_CFLAGS) $(CFLAGS) pgo-generate: LDFLAGS := $(RELEASE_LDFLAGS) $(LDFLAGS) pgo-generate: $(PGO_OBJ) @@ -107,6 +116,9 @@ $(PROJECT)-pgo: pgo-profile mv pgo-use $@ strip $@ + + clean: + cd fcmp && make clean rm -rf {obj,prof} - rm -f $(PROJECT)-{release,debug,pgo} + rm -f $(PROJECT)-{release,debug,pgo,rs} diff --git a/fcmprs/Cargo.toml b/fcmprs/Cargo.toml index 905a6bb..7cf524d 100644 --- a/fcmprs/Cargo.toml +++ b/fcmprs/Cargo.toml @@ -20,5 +20,6 @@ panic = "abort" [dependencies] cfg-if = "1.0.0" memmap = "0.7.0" +once_cell = "1.5.2" rayon = {version = "1.5.0", optional = true} smallvec = "1.5.0" diff --git a/fcmprs/src/main.rs b/fcmprs/src/main.rs index 3d04ead..e95b6da 100644 --- a/fcmprs/src/main.rs +++ b/fcmprs/src/main.rs @@ -1,3 +1,6 @@ + +#![allow(dead_code)] + #[cfg(feature="threads")] use rayon::prelude::*; use std::{ path::Path, @@ -18,9 +21,25 @@ mod error; use error::ResultPrintExt as _; mod map; +use map::MappedFile as _; +#[derive(Debug)] +/// There was a non-matching file struct UnmatchError; +const _:() = { + use std::{fmt,error}; + impl error::Error for UnmatchError{} + impl fmt::Display for UnmatchError + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "files did not match") + } + } + () +}; + fn main() { let (map1, rest) = { let mut args = std::env::args().skip(1); @@ -39,10 +58,9 @@ fn main() { let path = Path::new(&filename); if path.exists() && path.is_file() { map::map(path).discard_msg(format!("Failed to map file {}", filename)) - .or_else(|| { ok = false; None }) } else { eprintln!("File {} does not exist or is not a normal file", filename); - ok = false; + ok=false; None } }).collect(); @@ -57,7 +75,7 @@ fn main() { .map(|map| { if slice == map.as_slice() { Ok(()) - }else{ + } else { Err(UnmatchError) } }) diff --git a/fcmprs/src/map.rs b/fcmprs/src/map.rs index 6e8e5c6..5e83582 100644 --- a/fcmprs/src/map.rs +++ b/fcmprs/src/map.rs @@ -1,5 +1,26 @@ use super::*; +use once_cell::sync::OnceCell; + +pub trait MappedFile +{ + #[inline] fn as_slice(&self) -> &[u8] + { + &self.as_map()[..] + } + fn as_map(&self) -> &memmap::Mmap; + fn as_file(&self) -> &fs::File; +} + +pub trait MappedFileNew: MappedFile + Sized +{ + fn try_map(file: fs::File) -> io::Result; + #[inline] fn map(file: fs::File) -> Self + { + Self::try_map(file).unwrap() + } +} + /// Represents an open and memory mapped file #[derive(Debug)] pub struct MemMap @@ -8,23 +29,107 @@ pub struct MemMap file: fs::File, } -impl MemMap +impl MappedFile for MemMap { /// Get the memory mapped portion as a slice - #[inline] pub fn as_slice(&self) -> &[u8] - { + fn as_slice(&self) -> &[u8] { &self.map[..] } + fn as_map(&self) -> &memmap::Mmap { + &self.map + } + #[inline] fn as_file(&self) -> &fs::File { + &self.file + } +} +impl MappedFileNew for MemMap +{ + #[inline] fn try_map(file: fs::File) -> io::Result + { + Ok(MemMap { + map: unsafe { memmap::Mmap::map(&file)? }, + file, + }) + } } /// Attempt to map this file -pub fn map(file: impl AsRef) -> io::Result +pub fn map_with(file: &Path) -> io::Result { let file = OpenOptions::new() .read(true) .open(file)?; - Ok(MemMap { - map: unsafe { memmap::Mmap::map(&file)? }, - file, - }) + + M::try_map(file) +} + +/// Type container for memory map +pub type DefaultMapType = LazyMap; + +/// Attempt to map this file to the `DefaultMapType` +pub fn map(file: impl AsRef) -> io::Result +{ + map_with(file.as_ref()) +} + +/// An open and maybe mapped file +#[derive(Debug)] +pub struct LazyMap +{ + map: OnceCell, + file: fs::File, +} + +impl LazyMap +{ + #[inline(always)] fn get_map(&self) -> &memmap::Mmap + { + self.map.get_or_init(|| unsafe {memmap::Mmap::map(&self.file).expect("Lazy map failed")}) + } + + #[inline(always)] fn try_get_map(&self) -> io::Result<&memmap::Mmap> + { + self.map.get_or_try_init(|| unsafe {memmap::Mmap::map(&self.file)}) + } + + /// Is the memory mapped already? + #[inline] pub fn is_mapped(&self) -> bool + { + self.map.get().is_some() + } + + /// Get the mapped portion if it is mapped, attempting a map if not + #[inline] pub fn try_as_slice(&self) -> io::Result<&[u8]> + { + Ok(&self.try_get_map()?[..]) + } +} + +impl MappedFile for LazyMap +{ + /// Get the memory mapped portion as a slice + /// + /// Returns blank slice if mapping fails + #[inline] fn as_slice(&self) -> &[u8] + { + self.try_get_map() + .map(|x| &x[..]) + .unwrap_or(&[]) + } + fn as_map(&self) -> &memmap::Mmap { + self.map.get().unwrap() + } + #[inline] fn as_file(&self) -> &fs::File { + &self.file + } +} +impl MappedFileNew for LazyMap +{ + #[inline] fn try_map(file: fs::File) -> io::Result + { + Ok(LazyMap { + map: OnceCell::new(), + file, + }) + } }