this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at tangled-ci 171 lines 5.0 kB view raw
1use super::stream::{copy_stream, guard_file_output, open_input, prepare_output}; 2use crate::{ 3 progress::ProgressArgs, 4 utils::{ 5 CmprssInput, CmprssOutput, CommonArgs, CompressionLevelValidator, Compressor, 6 DefaultCompressionValidator, LevelArgs, Result, 7 }, 8}; 9use clap::Args; 10use std::io::{self, Write}; 11use xz2::read::XzDecoder; 12use xz2::stream::{LzmaOptions, Stream}; 13use xz2::write::XzEncoder; 14 15/// Memory limit passed to the LZMA decoder. `u64::MAX` disables the limit, 16/// which matches the behavior of `xz --lzma1 -d` / `unlzma`. 17const LZMA_DECODER_MEMLIMIT: u64 = u64::MAX; 18 19/// Swallows `flush()` calls on the wrapped writer. The legacy LZMA1 20/// (`lzma_alone`) encoder in liblzma rejects `LZMA_FULL_FLUSH`, which is what 21/// the inner `XzEncoder::flush` issues, so progress/copy helpers that call 22/// `flush` mid-stream must see a no-op flush and let `try_finish` (via Drop 23/// or `finish()`) finalize the stream instead. 24struct NoFlush<W>(W); 25 26impl<W: Write> Write for NoFlush<W> { 27 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 28 self.0.write(buf) 29 } 30 31 fn flush(&mut self) -> io::Result<()> { 32 Ok(()) 33 } 34} 35 36#[derive(Args, Debug)] 37pub struct LzmaArgs { 38 #[clap(flatten)] 39 pub common_args: CommonArgs, 40 41 #[clap(flatten)] 42 progress_args: ProgressArgs, 43 44 #[clap(flatten)] 45 pub level_args: LevelArgs, 46} 47 48#[derive(Clone)] 49pub struct Lzma { 50 pub level: i32, 51 pub progress_args: ProgressArgs, 52} 53 54impl Default for Lzma { 55 fn default() -> Self { 56 let validator = DefaultCompressionValidator; 57 Lzma { 58 level: validator.default_level(), 59 progress_args: ProgressArgs::default(), 60 } 61 } 62} 63 64impl Lzma { 65 pub fn new(args: &LzmaArgs) -> Lzma { 66 Lzma { 67 level: args.level_args.resolve(&DefaultCompressionValidator), 68 progress_args: args.progress_args, 69 } 70 } 71 72 /// Build a fresh LZMA1 (`lzma_alone`) encoder stream at the configured level. 73 fn encoder_stream(&self) -> Result<Stream> { 74 let options = LzmaOptions::new_preset(self.level as u32)?; 75 Ok(Stream::new_lzma_encoder(&options)?) 76 } 77 78 /// Build a fresh LZMA1 (`lzma_alone`) decoder stream. 79 fn decoder_stream() -> Result<Stream> { 80 Ok(Stream::new_lzma_decoder(LZMA_DECODER_MEMLIMIT)?) 81 } 82} 83 84impl Compressor for Lzma { 85 /// The standard extension for legacy LZMA (`.lzma`) files. 86 fn extension(&self) -> &str { 87 "lzma" 88 } 89 90 /// Full name for lzma. 91 fn name(&self) -> &str { 92 "lzma" 93 } 94 95 fn compress(&self, input: CmprssInput, output: CmprssOutput) -> Result { 96 guard_file_output(&output, "LZMA")?; 97 let (input_stream, file_size, pipeline_inner) = open_input(input, "LZMA")?; 98 let (writer, target) = prepare_output(output)?; 99 let mut encoder = XzEncoder::new_stream(writer, self.encoder_stream()?); 100 // `copy_stream` flushes the final writer on the path/pipe branch via 101 // `copy_with_progress`; LZMA1 (`lzma_alone`) rejects mid-stream flush, 102 // so wrap the encoder to swallow those calls and let `try_finish` 103 // finalize the stream. 104 copy_stream( 105 input_stream, 106 NoFlush(&mut encoder), 107 file_size, 108 pipeline_inner, 109 &self.progress_args, 110 target, 111 )?; 112 encoder.try_finish()?; 113 Ok(()) 114 } 115 116 fn extract(&self, input: CmprssInput, output: CmprssOutput) -> Result { 117 guard_file_output(&output, "LZMA")?; 118 let (input_stream, file_size, pipeline_inner) = open_input(input, "LZMA")?; 119 let decoder = XzDecoder::new_stream(input_stream, Self::decoder_stream()?); 120 let (writer, target) = prepare_output(output)?; 121 copy_stream( 122 decoder, 123 writer, 124 file_size, 125 pipeline_inner, 126 &self.progress_args, 127 target, 128 )?; 129 Ok(()) 130 } 131} 132 133#[cfg(test)] 134mod tests { 135 use super::*; 136 use crate::test_utils::*; 137 138 /// Test the basic interface of the Lzma compressor 139 #[test] 140 fn test_lzma_interface() { 141 let compressor = Lzma::default(); 142 test_compressor_interface(&compressor, "lzma", Some("lzma")); 143 } 144 145 /// Test the default compression level 146 #[test] 147 fn test_lzma_default_compression() -> Result { 148 let compressor = Lzma::default(); 149 test_compression(&compressor) 150 } 151 152 /// Test fast compression level 153 #[test] 154 fn test_lzma_fast_compression() -> Result { 155 let fast_compressor = Lzma { 156 level: 1, 157 progress_args: ProgressArgs::default(), 158 }; 159 test_compression(&fast_compressor) 160 } 161 162 /// Test best compression level 163 #[test] 164 fn test_lzma_best_compression() -> Result { 165 let best_compressor = Lzma { 166 level: 9, 167 progress_args: ProgressArgs::default(), 168 }; 169 test_compression(&best_compressor) 170 } 171}