diff --git a/Cargo.toml b/Cargo.toml index b1f12c0..7453c0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ codegen-units = 1 panic = "unwind" [features] -default = ["splash", "inspect", "defer-drop", "jemalloc", "prealloc"] +default = ["splash", "inspect", "defer-drop", "jemalloc"] # Use jemalloc as global allocator instead of system allocator. # May potentially cause some speedups and better memory profile on large runs. diff --git a/src/arg.rs b/src/arg.rs index 2f357ff..9694acb 100644 --- a/src/arg.rs +++ b/src/arg.rs @@ -33,7 +33,8 @@ const OPTIONS_NORMAL: &'static [&'static str] = &[ "-v Verbose mode. Output extra information.", #[cfg(feature="inspect")] "--save Dump the collected data to this file for further inspection.", #[cfg(feature="inspect")] "-D Dump the collected data to `stdout` (see `--save`.)", - #[cfg(feature="prealloc")] "--save-raw Dump the collected data to this file uncompressed.", + #[cfg(feature="inspect")] "--save-raw Dump the collected data to this file uncompressed. (see `--save`.)", + #[cfg(feature="inspect")] "-Dr Dump the collected data to standard output uncompressed. (see `--save-raw`.)", "- Stop parsing arguments, treat all the rest as paths.", ]; @@ -143,15 +144,23 @@ fn parse>(args: I) -> eyre::Result #[cfg(feature="inspect")] "-D" => { cfg.serialise_output = Some(config::OutputSerialisationMode::Stdout); }, + #[cfg(feature="inspect")] "-Dr" => { + cfg.serialise_output = Some(config::OutputSerialisationMode::RawStdout); + }, #[cfg(feature="inspect")] "--save" => { let file = args.next().ok_or(eyre!("`--save` expects a filename parameter")) .with_suggestion(suggestion_intended_arg.clone())?; cfg.serialise_output = Some(config::OutputSerialisationMode::File(file.into())); }, - #[cfg(feature="prealloc")] "--save-raw" => { + #[cfg(feature="inspect")] "--save-raw" => { let file = args.next().ok_or(eyre!("`--save-raw` expects a filename parameter")) .with_suggestion(suggestion_intended_arg.clone())?; - cfg.serialise_output = Some(config::OutputSerialisationMode::PreallocFile(file.into())); + #[cfg(feature="prealloc")] { + cfg.serialise_output = Some(config::OutputSerialisationMode::PreallocFile(file.into())); + } + #[cfg(not(feature="prealloc"))] { + cfg.serialise_output = Some(config::OutputSerialisationMode::RawFile(file.into())); + } }, "--recursive" => { let max = args.next().ok_or(eyre!("`--recursive` expects a parameter")) diff --git a/src/config.rs b/src/config.rs index 82376ef..98f2fb8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -98,13 +98,29 @@ impl From for Recursion #[cfg(feature="inspect")] #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub enum OutputSerialisationMode { Stdout, File(PathBuf), + RawFile(PathBuf), + RawStdout, #[cfg(feature="prealloc")] PreallocFile(PathBuf), } +#[cfg(feature="inspect")] +impl OutputSerialisationMode +{ + /// Should this serialisation mode be compressed? + #[inline] pub fn should_compress(&self) -> bool + { + match self { + Self::File(_) | Self::Stdout => true, + _ => false, + } + } +} + /// Configuration for this run #[derive(Debug, Clone, PartialEq, Eq)] pub struct Config @@ -139,11 +155,11 @@ impl Config #[inline] pub fn is_using_stdout(&self) -> bool { #[cfg(feature="inspect")] { - return if let Some(OutputSerialisationMode::Stdout) = self.serialise_output { - true - } else { - false - }; + return match self.serialise_output { + Some(OutputSerialisationMode::Stdout) | + Some(OutputSerialisationMode::RawStdout) => true, + _ => false, + } } #[cfg(not(feature="inspect"))] { false diff --git a/src/main.rs b/src/main.rs index b4cfba8..628ad55 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,8 +73,10 @@ async fn normal(cfg: config::Config) -> eyre::Result<()> type BoxedWrite = Box; use futures::FutureExt; use config::OutputSerialisationMode; + let should_comp = ser_out.should_compress(); match ser_out { - OutputSerialisationMode::File(output_file) => { + OutputSerialisationMode::File(output_file) | + OutputSerialisationMode::RawFile(output_file) => { use tokio::fs::OpenOptions; (async move { let stream = OpenOptions::new() @@ -86,25 +88,27 @@ async fn normal(cfg: config::Config) -> eyre::Result<()> .with_section(|| format!("{:?}", output_file).header("File was"))?; Ok::(Box::new(stream)) - }.boxed(), None) + }.boxed(), None, should_comp) }, - OutputSerialisationMode::Stdout => (async move { Ok::(Box::new(tokio::io::stdout())) }.boxed(), None), + OutputSerialisationMode::Stdout | + OutputSerialisationMode::RawStdout => (async move { Ok::(Box::new(tokio::io::stdout())) }.boxed(), Option::<&std::path::PathBuf>::None, should_comp), #[cfg(feature="prealloc")] OutputSerialisationMode::PreallocFile(output_file) => { (async move { Ok::(Box::new(tokio::io::sink())) // we use a sink as a shim stream since it will never be used when tuple item `.1` is Some() - }.boxed(), Some(output_file)) + }.boxed(), Some(output_file), false) }, } }) { // We use tuple item `.1` here to indicate if we're in normal write mode. // None -> normal // Some(path) -> prealloc - Some((stream_fut, None)) => { + // `.2` indicates if we should compress while in normal write mode. + Some((stream_fut, None, compress)) => { let stream = stream_fut.await?; - serial::write_async(stream, &graph, true).await // TODO: raw version + serial::write_async(stream, &graph, compress).await // TODO: raw version, how to implement? .wrap_err(eyre!("Failed to serialise graph to stream"))?; }, - #[cfg(feature="prealloc")] Some((_task_fut, Some(output_file))) => { + #[cfg(feature="prealloc")] Some((_task_fut, Some(output_file), _)) => { use tokio::fs::OpenOptions; let file = OpenOptions::new() .write(true) diff --git a/src/serial.rs b/src/serial.rs index 47f3b69..1bdefeb 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -17,18 +17,49 @@ const DEFER_DROP_SIZE_FLOOR: usize = 1024 * 1024; // 1 MB enum MaybeCompressor<'a, T> { Compressing(Compressor<&'a mut T>), + Decompressing(Decompressor<&'a mut T>), Raw(&'a mut T), } +/// Compress or decompress? +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)] +enum CompKind +{ + Compress, + Decompress +} + +impl Default for CompKind +{ + #[inline] + fn default() -> Self + { + Self::Compress + } +} + +impl<'a, T> MaybeCompressor<'a, T> +{ + /// What kind is this compressor set to + pub fn kind(&self) -> Option + { + Some(match self { + Self::Raw(_) => return None, + Self::Compressing(_) => CompKind::Compress, + Self::Decompressing(_) => CompKind::Decompress, + }) + } +} + impl<'a, T> MaybeCompressor<'a, T> where T: AsyncWrite + Unpin + 'a { - pub fn new(raw: &'a mut T, compress: bool) -> Self + pub fn new(raw: &'a mut T, compress: Option) -> Self { - if compress { - Self::Compressing(Compressor::new(raw)) - } else { - Self::Raw(raw) + match compress { + Some(CompKind::Compress) => Self::Compressing(Compressor::new(raw)), + Some(CompKind::Decompress) => Self::Decompressing(Decompressor::new(raw)), + None => Self::Raw(raw), } } } @@ -39,6 +70,7 @@ where T: AsyncWrite + Unpin + 'a fn deref_mut(&mut self) -> &mut Self::Target { match self { Self::Compressing(t) => t, + Self::Decompressing(t) => t, Self::Raw(o) => o, } } @@ -51,6 +83,7 @@ where T: AsyncWrite + Unpin + 'a fn deref(&self) -> &Self::Target { match self { Self::Compressing(t) => t, + Self::Decompressing(t) => t, Self::Raw(o) => o, } } @@ -73,7 +106,7 @@ where W: AsyncWrite + Unpin .with_section(sect_type_name.clone())?; { - let mut stream = MaybeCompressor::new(&mut to, compress); + let mut stream = MaybeCompressor::new(&mut to, compress.then(|| CompKind::Compress)); cfg_eprintln!(Verbose; config::get_global(), "Writing {} bytes of type {:?} to stream of type {:?}", vec.len(), std::any::type_name::(), std::any::type_name::());