diff --git a/Cargo.toml b/Cargo.toml index 4865f8f..6f5bed8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ exclude = [ debug=true [dependencies] -anyhow = { version="1.0", features = ["backtrace"]} byteorder = "1.4" cabac = "0.6.0" default-boxed = "0.2" diff --git a/src/complevel_estimator.rs b/src/complevel_estimator.rs index 93e32d3..dc4d5a2 100644 --- a/src/complevel_estimator.rs +++ b/src/complevel_estimator.rs @@ -12,6 +12,7 @@ use crate::{ depth_estimator::{new_depth_estimator, HashTableDepthEstimator}, hash_algorithm::HashAlgorithm, preflate_constants, + preflate_error::{err_exit_code, ExitCode, Result}, preflate_input::PreflateInput, preflate_parse_config::{ MatchingType, SLOW_PREFLATE_PARSER_SETTINGS, ZLIB_PREFLATE_PARSER_SETTINGS, @@ -240,9 +241,9 @@ impl<'a> CompLevelEstimatorState<'a> { } } - fn recommend(&mut self) -> anyhow::Result { + fn recommend(&mut self) -> Result { if self.candidates.is_empty() { - return Err(anyhow::anyhow!("no candidates found")); + return err_exit_code(ExitCode::PredictBlock, "no candidates found"); } let candidate = self @@ -284,10 +285,10 @@ impl<'a> CompLevelEstimatorState<'a> { } if candidate.max_chain_found() >= 4096 { - return Err(anyhow::anyhow!( - "max_chain_found too large: {}", - candidate.max_chain_found() - )); + return err_exit_code( + ExitCode::PredictBlock, + format!("max_chain_found too large: {}", candidate.max_chain_found()).as_str(), + ); } let very_far_matches = longest_dist_at_hop_0 @@ -325,7 +326,7 @@ pub fn estimate_preflate_comp_level( plain_text: &[u8], add_policy: DictionaryAddPolicy, blocks: &Vec, -) -> anyhow::Result { +) -> Result { let mut state = CompLevelEstimatorState::new(wbits, mem_level, plain_text, add_policy, min_len, blocks); state.check_dump(); diff --git a/src/deflate_reader.rs b/src/deflate_reader.rs index f93c125..570dcd0 100644 --- a/src/deflate_reader.rs +++ b/src/deflate_reader.rs @@ -4,7 +4,7 @@ * This software incorporates material from third parties. See NOTICE.txt for details. *--------------------------------------------------------------------------------------------*/ -use anyhow::Context; +use crate::preflate_error::{err_exit_code, AddContext, ExitCode, Result}; use std::io::Read; @@ -41,7 +41,7 @@ impl DeflateReader { std::mem::take(&mut self.plain_text) } - fn read_bit(&mut self) -> anyhow::Result { + fn read_bit(&mut self) -> Result { Ok(self.input.get(1)? != 0) } @@ -61,7 +61,7 @@ impl DeflateReader { } } - pub fn read_block(&mut self, last: &mut bool) -> anyhow::Result { + pub fn read_block(&mut self, last: &mut bool) -> Result { let mut blk; *last = self.read_bit()?; @@ -77,7 +77,7 @@ impl DeflateReader { let len = self.read_bits(16)?; let ilen = self.read_bits(16)?; if (len ^ ilen) != 0xffff { - return Err(anyhow::Error::msg("Blocllength mismatch")); + return err_exit_code(ExitCode::AnalyzeFailed, "Block length mismatch"); } blk.context_len = 0; @@ -105,12 +105,11 @@ impl DeflateReader { let decoder = HuffmanReader::create_from_original_encoding(&blk.huffman_encoding)?; - self.decode_block(&decoder, &mut blk) - .with_context(|| "decode_block dyn")?; + self.decode_block(&decoder, &mut blk).context()?; Ok(blk) } - _ => Err(anyhow::Error::msg("Invalid block type")), + _ => err_exit_code(ExitCode::InvalidDeflate, "Invalid block type"), } } @@ -118,7 +117,7 @@ impl DeflateReader { &mut self, decoder: &HuffmanReader, blk: &mut PreflateTokenBlock, - ) -> anyhow::Result<()> { + ) -> Result<()> { let mut earliest_reference = i32::MAX; let mut cur_pos = 0; @@ -134,7 +133,7 @@ impl DeflateReader { } else { let lcode: u32 = lit_len - preflate_constants::NONLEN_CODE_COUNT as u32; if lcode >= preflate_constants::LEN_CODE_COUNT as u32 { - return Err(anyhow::Error::msg("Invalid length code")); + return err_exit_code(ExitCode::InvalidDeflate, "Invalid length code"); } let len: u32 = preflate_constants::MIN_MATCH + preflate_constants::LENGTH_BASE_TABLE[lcode as usize] as u32 @@ -147,14 +146,14 @@ impl DeflateReader { let dcode = decoder.fetch_next_distance_char(&mut self.input)? as u32; if dcode >= preflate_constants::DIST_CODE_COUNT as u32 { - return Err(anyhow::Error::msg("Invalid distance code")); + return err_exit_code(ExitCode::InvalidDeflate, "Invalid distance code"); } let dist = 1 + preflate_constants::DIST_BASE_TABLE[dcode as usize] as u32 + self .read_bits(preflate_constants::DIST_EXTRA_TABLE[dcode as usize].into())?; if dist as usize > self.plain_text.len() { - return Err(anyhow::Error::msg("Invalid distance")); + return err_exit_code(ExitCode::InvalidDeflate, "Invalid distance"); } self.write_reference(dist, len); blk.add_reference(len, dist, irregular_258); diff --git a/src/deflate_writer.rs b/src/deflate_writer.rs index 5e3a8dd..39d8358 100644 --- a/src/deflate_writer.rs +++ b/src/deflate_writer.rs @@ -4,7 +4,7 @@ * This software incorporates material from third parties. See NOTICE.txt for details. *--------------------------------------------------------------------------------------------*/ -use anyhow::Result; +use crate::preflate_error::Result; use crate::{ bit_writer::BitWriter, diff --git a/src/depth_estimator.rs b/src/depth_estimator.rs index 6afd1c3..b7936fa 100644 --- a/src/depth_estimator.rs +++ b/src/depth_estimator.rs @@ -15,7 +15,7 @@ pub trait HashTableDepthEstimator { } #[derive(DefaultBoxed)] -pub struct HashTableDepthEstimatorImpl { +struct HashTableDepthEstimatorImpl { /// Represents the head of the hash chain for a given hash value. head: [u16; 65536], diff --git a/src/hash_chain_holder.rs b/src/hash_chain_holder.rs index 18bec33..275e452 100644 --- a/src/hash_chain_holder.rs +++ b/src/hash_chain_holder.rs @@ -11,10 +11,12 @@ use crate::hash_algorithm::{ }; use crate::hash_chain::{HashChain, MAX_UPDATE_HASH_BATCH}; use crate::preflate_constants::{MAX_MATCH, MIN_LOOKAHEAD, MIN_MATCH}; +use crate::preflate_error::{err_exit_code, ExitCode, Result}; use crate::preflate_input::PreflateInput; use crate::preflate_parameter_estimator::PreflateStrategy; use crate::preflate_token::PreflateTokenReference; use crate::token_predictor::TokenPredictorParameters; + use std::cmp; #[derive(Debug, Copy, Clone)] @@ -85,11 +87,11 @@ pub trait HashChainHolder { &self, target_reference: &PreflateTokenReference, input: &PreflateInput, - ) -> anyhow::Result; + ) -> Result; /// Does the inverse of calculate_hops, where we start from the predicted token and /// get the new distance based on the number of hops - fn hop_match(&self, len: u32, hops: u32, input: &PreflateInput) -> anyhow::Result; + fn hop_match(&self, len: u32, hops: u32, input: &PreflateInput) -> Result; /// debugging function to verify that the hash chain is correct fn verify_hash(&self, _dist: Option); @@ -124,11 +126,11 @@ impl HashChainHolder for () { &self, _target_reference: &PreflateTokenReference, _input: &PreflateInput, - ) -> anyhow::Result { + ) -> Result { unimplemented!() } - fn hop_match(&self, _len: u32, _hops: u32, _input: &PreflateInput) -> anyhow::Result { + fn hop_match(&self, _len: u32, _hops: u32, _input: &PreflateInput) -> Result { unimplemented!() } @@ -171,11 +173,11 @@ impl HashChainHolder for HashChainHolderImpl { &self, target_reference: &PreflateTokenReference, input: &PreflateInput, - ) -> anyhow::Result { + ) -> Result { let max_len = std::cmp::min(input.remaining(), MAX_MATCH); if max_len < target_reference.len() { - return Err(anyhow::anyhow!("max_len < target_reference.len()")); + return err_exit_code(ExitCode::PredictBlock, "max_len < target_reference.len()"); } let max_chain_org = 0xffff; // max hash chain length @@ -213,15 +215,15 @@ impl HashChainHolder for HashChainHolderImpl { max_chain -= 1; } - Err(anyhow::anyhow!("no match found")) + err_exit_code(ExitCode::MatchNotFound, "no match found") } /// Does the inverse of calculate_hops, where we start from the predicted token and /// get the new distance based on the number of hops - fn hop_match(&self, len: u32, hops: u32, input: &PreflateInput) -> anyhow::Result { + fn hop_match(&self, len: u32, hops: u32, input: &PreflateInput) -> Result { let max_len = std::cmp::min(input.remaining(), MAX_MATCH); if max_len < len { - return Err(anyhow::anyhow!("not enough data left to match")); + return err_exit_code(ExitCode::RecompressFailed, "not enough data left to match"); } let cur_max_dist = std::cmp::min(input.pos(), self.window_bytes); @@ -247,7 +249,7 @@ impl HashChainHolder for HashChainHolderImpl { } } - Err(anyhow::anyhow!("no match found")) + err_exit_code(ExitCode::MatchNotFound, "no match found") } /// debugging function to verify that the hash chain is correct diff --git a/src/huffman_encoding.rs b/src/huffman_encoding.rs index 826d8ba..a00198a 100644 --- a/src/huffman_encoding.rs +++ b/src/huffman_encoding.rs @@ -4,7 +4,7 @@ * This software incorporates material from third parties. See NOTICE.txt for details. *--------------------------------------------------------------------------------------------*/ -use anyhow::Result; +use crate::preflate_error::{err_exit_code, ExitCode, Result}; use crate::{ bit_reader::ReadBits, @@ -48,7 +48,7 @@ impl HuffmanOriginalEncoding { /// Reads a dynamic huffman table from the bit reader. The structure /// holds all the information necessary to recode the huffman table /// exactly as it was written. - pub fn read(bit_reader: &mut R) -> anyhow::Result { + pub fn read(bit_reader: &mut R) -> Result { // 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286) let hlit = bit_reader.get(5)? as usize + 257; // 5 Bits: HDIST, # of Distance codes - 1 (1 - 32) @@ -92,7 +92,9 @@ impl HuffmanOriginalEncoding { 16 => TreeCodeType::Repeat, 17 => TreeCodeType::ZeroShort, 18 => TreeCodeType::ZeroLong, - _ => return Err(anyhow::Error::msg("Invalid code length")), + _ => { + return err_exit_code(ExitCode::InvalidDeflate, "Invalid code length"); + } }; let (sub, bits) = Self::get_tree_code_adjustment(tree_code); @@ -105,9 +107,10 @@ impl HuffmanOriginalEncoding { } if codes_read != c_lengths_combined { - return Err(anyhow::Error::msg( + return err_exit_code( + ExitCode::InvalidDeflate, "Code table should be same size as hdist + hlit", - )); + ); } Ok(HuffmanOriginalEncoding { @@ -120,11 +123,7 @@ impl HuffmanOriginalEncoding { } /// writes dynamic huffman table to the output buffer using the bitwriter - pub fn write( - &self, - bitwriter: &mut BitWriter, - output_buffer: &mut Vec, - ) -> anyhow::Result<()> { + pub fn write(&self, bitwriter: &mut BitWriter, output_buffer: &mut Vec) -> Result<()> { bitwriter.write(self.num_literals as u32 - 257, 5, output_buffer); bitwriter.write(self.num_dist as u32 - 1, 5, output_buffer); bitwriter.write(self.num_code_lengths as u32 - 4, 4, output_buffer); @@ -265,7 +264,7 @@ impl HuffmanReader { /// clarity. Literal/length values 286-287 will never actually /// occur in the compressed data, but participate in the code /// construction. - pub fn create_fixed() -> anyhow::Result { + pub fn create_fixed() -> Result { let (lit_lengths, dist_lengths) = HuffmanOriginalEncoding::get_fixed_distance_lengths(); Ok(HuffmanReader { @@ -286,11 +285,11 @@ impl HuffmanReader { }) } - pub fn fetch_next_literal_code(&self, bit_reader: &mut R) -> anyhow::Result { + pub fn fetch_next_literal_code(&self, bit_reader: &mut R) -> Result { decode_symbol(bit_reader, &self.lit_huff_code_tree) } - pub fn fetch_next_distance_char(&self, bit_reader: &mut R) -> anyhow::Result { + pub fn fetch_next_distance_char(&self, bit_reader: &mut R) -> Result { decode_symbol(bit_reader, &self.dist_huff_code_tree) } } diff --git a/src/huffman_helper.rs b/src/huffman_helper.rs index 3dbb9eb..4f9f741 100644 --- a/src/huffman_helper.rs +++ b/src/huffman_helper.rs @@ -5,10 +5,11 @@ *--------------------------------------------------------------------------------------------*/ use crate::bit_reader::ReadBits; +use crate::preflate_error::{err_exit_code, ExitCode, Result}; use std::vec; /// Calculates Huffman code array given an array of Huffman Code Lengths using the RFC 1951 algorithm -pub fn calc_huffman_codes(code_lengths: &[u8]) -> anyhow::Result> { +pub fn calc_huffman_codes(code_lengths: &[u8]) -> Result> { let mut result: Vec = vec![0; code_lengths.len()]; // The following algorithm generates the codes as integers, intended to be read @@ -100,9 +101,9 @@ fn is_valid_huffman_code_lengths(code_lengths: &[u8]) -> bool { /// rg_huff_nodes[N+1] is the array index of the '1' child /// 2. If rg_huff_nodes[i] is less than zero then it is a leaf and the literal alphabet value is -rg_huff_nodes[i] + 1 /// 3. The root node index 'N' is rg_huff_nodes.len() - 2. Search should start at that node. -pub fn calculate_huffman_code_tree(code_lengths: &[u8]) -> anyhow::Result> { +pub fn calculate_huffman_code_tree(code_lengths: &[u8]) -> Result> { if !is_valid_huffman_code_lengths(code_lengths) { - return Err(anyhow::anyhow!("Invalid Huffman code lengths")); + return err_exit_code(ExitCode::InvalidDeflate, "Invalid Huffman code lengths"); } let mut c_codes: i32 = 0; @@ -152,7 +153,7 @@ pub fn calculate_huffman_code_tree(code_lengths: &[u8]) -> anyhow::Result(bit_reader: &mut R, huffman_tree: &[i32]) -> anyhow::Result { +pub fn decode_symbol(bit_reader: &mut R, huffman_tree: &[i32]) -> Result { let mut i_node_cur: i32 = huffman_tree.len() as i32 - 2; // Start at the root of the Huffman tree loop { diff --git a/src/idat_parse.rs b/src/idat_parse.rs index d460dbe..87bd418 100644 --- a/src/idat_parse.rs +++ b/src/idat_parse.rs @@ -1,8 +1,11 @@ use std::io::{Read, Write}; -use anyhow::Result; +use crate::preflate_error::Result; -use crate::preflate_container::{read_varint, write_varint}; +use crate::{ + preflate_container::{read_varint, write_varint}, + preflate_error::{err_exit_code, ExitCode}, +}; #[derive(Debug, PartialEq)] pub struct IdatContents { @@ -88,7 +91,7 @@ pub fn parse_idat( deflate_info_dump_level: u32, ) -> Result<(IdatContents, Vec)> { if png_idat_stream.len() < 12 || &png_idat_stream[4..8] != b"IDAT" { - return Err(anyhow::Error::msg("No IDAT chunk found")); + return err_exit_code(ExitCode::InvalidIDat, "No IDAT chunk found"); } let mut deflate_stream = Vec::new(); @@ -129,7 +132,7 @@ pub fn parse_idat( png_idat_stream[pos + chunk_len + 11], ]) { - return Err(anyhow::Error::msg("CRC mismatch")); + return err_exit_code(ExitCode::InvalidIDat, "CRC mismatch"); } idat_chunk_sizes.push(chunk_len as u32); @@ -141,7 +144,7 @@ pub fn parse_idat( } if deflate_stream.len() < 3 { - return Err(anyhow::Error::msg("No IDAT data found")); + return err_exit_code(ExitCode::InvalidIDat, "No IDAT data found"); } // remove the zlib header since it can be somewhat arbitary so we store it seperately @@ -175,9 +178,10 @@ pub fn recreate_idat( ) -> Result<()> { // the total length of the chunks is the sum of the chunk sizes + 2 bytes for the zlib header + 4 bytes for the addler32 if idat.chunk_sizes.iter().sum::() as usize != deflate_stream.len() + 2 + 4 { - return Err(anyhow::Error::msg( + return err_exit_code( + ExitCode::InvalidIDat, "Chunk sizes do not match deflate stream length", - )); + ); } let mut index = 0; diff --git a/src/preflate_container.rs b/src/preflate_container.rs index 43f8f91..576288b 100644 --- a/src/preflate_container.rs +++ b/src/preflate_container.rs @@ -5,7 +5,7 @@ use std::io::{Cursor, Read, Write}; use crate::{ cabac_codec::{PredictionDecoderCabac, PredictionEncoderCabac}, idat_parse::{recreate_idat, IdatContents}, - preflate_error::{ExitCode, PreflateError}, + preflate_error::{AddContext, ExitCode, PreflateError}, preflate_input::PreflateInput, preflate_parameter_estimator::{estimate_preflate_parameters, PreflateParameters}, process::{decode_mispredictions, encode_mispredictions, parse_deflate}, @@ -152,8 +152,7 @@ fn read_chunk_block( let recompressed = recompress_deflate_stream(&segment, &corrections)?; if let Some(idat) = idat { - recreate_idat(&idat, &recompressed[..], destination) - .map_err(|e| PreflateError::wrap(ExitCode::InvalidCompressedWrapper, &e))?; + recreate_idat(&idat, &recompressed[..], destination).context()?; } else { destination.write_all(&recompressed)?; } @@ -352,8 +351,7 @@ pub fn decompress_deflate_stream( //process::write_file("c:\\temp\\lastop.deflate", compressed_data); //process::write_file("c:\\temp\\lastop.bin", contents.plain_text.as_slice()); - let params = estimate_preflate_parameters(&contents.plain_text, &contents.blocks) - .map_err(|e| PreflateError::wrap(ExitCode::AnalyzeFailed, &e))?; + let params = estimate_preflate_parameters(&contents.plain_text, &contents.blocks).context()?; if loglevel > 0 { println!("params: {:?}", params); @@ -364,12 +362,15 @@ pub fn decompress_deflate_stream( cabac_encoder.finish(); + if loglevel > 0 { + cabac_encoder.print(); + } + if verify { let mut cabac_decoder = PredictionDecoderCabac::new(VP8Reader::new(Cursor::new(&cabac_encoded[..])).unwrap()); - let reread_params = PreflateParameters::read(&mut cabac_decoder) - .map_err(|e| PreflateError::wrap(ExitCode::InvalidPredictionData, &e))?; + let reread_params = PreflateParameters::read(&mut cabac_decoder).context()?; assert_eq!(params, reread_params); let (recompressed, _recreated_blocks) = decode_mispredictions( @@ -402,8 +403,7 @@ pub fn recompress_deflate_stream( let mut cabac_decoder = PredictionDecoderCabac::new(VP8Reader::new(Cursor::new(prediction_corrections)).unwrap()); - let params = PreflateParameters::read(&mut cabac_decoder) - .map_err(|e| PreflateError::wrap(ExitCode::InvalidPredictionData, &e))?; + let params = PreflateParameters::read(&mut cabac_decoder).context()?; let (recompressed, _recreated_blocks) = decode_mispredictions(¶ms, PreflateInput::new(plain_text), &mut cabac_decoder)?; Ok(recompressed) @@ -418,6 +418,8 @@ pub fn decompress_deflate_stream_assert( ) -> Result { use cabac::debug::{DebugReader, DebugWriter}; + use crate::preflate_error::AddContext; + let mut cabac_encoded = Vec::new(); let mut cabac_encoder = @@ -425,8 +427,7 @@ pub fn decompress_deflate_stream_assert( let contents = parse_deflate(compressed_data, 0)?; - let params = estimate_preflate_parameters(&contents.plain_text, &contents.blocks) - .map_err(|e| PreflateError::wrap(ExitCode::AnalyzeFailed, &e))?; + let params = estimate_preflate_parameters(&contents.plain_text, &contents.blocks).context()?; params.write(&mut cabac_encoder); encode_mispredictions(&contents, ¶ms, &mut cabac_encoder)?; @@ -438,8 +439,7 @@ pub fn decompress_deflate_stream_assert( let mut cabac_decoder = PredictionDecoderCabac::new(DebugReader::new(Cursor::new(&cabac_encoded)).unwrap()); - let params = PreflateParameters::read(&mut cabac_decoder) - .map_err(|e| PreflateError::wrap(ExitCode::InvalidPredictionData, &e))?; + let params = PreflateParameters::read(&mut cabac_decoder)?; let (recompressed, _recreated_blocks) = decode_mispredictions( ¶ms, PreflateInput::new(&contents.plain_text), diff --git a/src/preflate_error.rs b/src/preflate_error.rs index 12e4d3a..68ed4e4 100644 --- a/src/preflate_error.rs +++ b/src/preflate_error.rs @@ -4,18 +4,9 @@ * This software incorporates material from third parties. See NOTICE.txt for details. *--------------------------------------------------------------------------------------------*/ -use std::{fmt::Display, io::ErrorKind}; - -use anyhow::Error; - -#[derive(Debug, Clone)] -pub struct PreflateError { - /// standard error code - exit_code: ExitCode, - - /// diagnostic message including location. Content should not be relied on. - message: String, -} +use std::fmt::Display; +use std::io::ErrorKind; +use std::num::TryFromIntError; #[derive(Debug, Clone, Copy, PartialEq)] #[allow(dead_code)] @@ -38,25 +29,9 @@ pub enum ExitCode { ShortRead = 16, OsError = 17, GeneralFailure = 18, -} - -/// translates std::io::Error into LeptonError -impl From for PreflateError { - #[track_caller] - fn from(e: std::io::Error) -> Self { - match e.downcast::() { - Ok(le) => { - return le; - } - Err(e) => { - let caller = std::panic::Location::caller(); - return PreflateError { - exit_code: get_io_error_exit_code(&e), - message: format!("error {} at {}", e.to_string(), caller.to_string()), - }; - } - } - } + InvalidIDat = 19, + MatchNotFound = 20, + InvalidDeflate = 21, } impl Display for ExitCode { @@ -65,131 +40,160 @@ impl Display for ExitCode { } } -impl Display for PreflateError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{0}: {1}", self.exit_code, self.message) +impl ExitCode { + /// Converts the error code into an integer for use as an error code when + /// returning from a C API. + pub fn as_integer_error_code(self) -> i32 { + self as i32 } } -impl From for PreflateError { - fn from(mut error: anyhow::Error) -> Self { - // first see if there is a LeptonError already inside - match error.downcast::() { - Ok(le) => { - return le; - } - Err(old_error) => { - error = old_error; - } - } - - // capture the original error string before we lose it due - // to downcasting to look for stashed LeptonErrors - let original_string = error.to_string(); - - // see if there is a LeptonError hiding inside an io error - // which happens if we cross an API boundary that returns an std::io:Error - // like Read or Write - match error.downcast::() { - Ok(ioe) => match ioe.downcast::() { - Ok(le) => { - return le; - } - Err(e) => { - return PreflateError { - exit_code: get_io_error_exit_code(&e), - message: format!("{} {}", e, original_string), - }; - } - }, - Err(_) => {} - } +/// Since errors are rare and stop everything, we want them to be as lightweight as possible. +#[derive(Debug, Clone)] +struct PreflateErrorInternal { + exit_code: ExitCode, + message: String, +} - // don't know what we got, so treat it as a general failure - return PreflateError { - exit_code: ExitCode::GeneralFailure, - message: original_string, - }; - } +/// Standard error returned by Preflate library +#[derive(Debug, Clone)] +pub struct PreflateError { + i: Box, } -fn get_io_error_exit_code(e: &std::io::Error) -> ExitCode { - if e.kind() == ErrorKind::UnexpectedEof { - ExitCode::ShortRead - } else { - ExitCode::OsError +pub type Result = std::result::Result; + +impl Display for PreflateError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{0}: {1}", self.i.exit_code, self.i.message) } } impl PreflateError { pub fn new(exit_code: ExitCode, message: &str) -> PreflateError { PreflateError { - exit_code, - message: message.to_owned(), + i: Box::new(PreflateErrorInternal { + exit_code, + message: message.to_owned(), + }), } } - pub fn error_on_block(exit_code: ExitCode, block_number: usize, e: &Error) -> PreflateError { - PreflateError { - exit_code, - message: format!("Error on block {}: {}", block_number, e), - } + pub fn exit_code(&self) -> ExitCode { + self.i.exit_code } - pub fn wrap(exit_code: ExitCode, e: &impl Display) -> PreflateError { - PreflateError { - exit_code, - message: e.to_string(), + pub fn message(&self) -> &str { + &self.i.message + } + + #[cold] + #[inline(never)] + #[track_caller] + pub fn add_context(&mut self) { + self.i + .message + .push_str(&format!("\n at {}", std::panic::Location::caller())); + } +} + +#[cold] +#[track_caller] +pub fn err_exit_code(error_code: ExitCode, message: &str) -> Result { + let mut e = PreflateError::new(error_code, message); + e.add_context(); + return Err(e); +} + +pub trait AddContext { + #[track_caller] + fn context(self) -> Result; + fn with_context String>(self, f: FN) -> Result; +} + +impl> AddContext for core::result::Result { + #[track_caller] + fn context(self) -> Result { + match self { + Ok(x) => Ok(x), + Err(e) => { + let mut e = e.into(); + e.add_context(); + Err(e) + } } } - pub fn wrap_anyhow(exit_code: ExitCode, e: &anyhow::Error) -> PreflateError { - PreflateError { - exit_code, - message: e.to_string(), + #[track_caller] + fn with_context String>(self, f: FN) -> Result { + match self { + Ok(x) => Ok(x), + Err(e) => { + let mut e = e.into(); + e.i.message.push_str(&f()); + e.add_context(); + Err(e) + } } } +} - pub fn exit_code(&self) -> ExitCode { - self.exit_code +impl std::error::Error for PreflateError {} + +fn get_io_error_exit_code(e: &std::io::Error) -> ExitCode { + if e.kind() == ErrorKind::UnexpectedEof { + ExitCode::ShortRead + } else { + ExitCode::OsError } +} - pub fn message(&self) -> &str { - &self.message +impl From for PreflateError { + #[track_caller] + fn from(e: TryFromIntError) -> Self { + let mut e = PreflateError::new(ExitCode::GeneralFailure, e.to_string().as_str()); + e.add_context(); + e + } +} + +/// translates std::io::Error into PreflateError +impl From for PreflateError { + #[track_caller] + fn from(e: std::io::Error) -> Self { + match e.downcast::() { + Ok(le) => { + return le; + } + Err(e) => { + let mut e = PreflateError::new(get_io_error_exit_code(&e), e.to_string().as_str()); + e.add_context(); + e + } + } } } -/// translates LeptonError into std::io::Error, which involves putting into a Box and using Other +/// translates PreflateError into std::io::Error, which involves putting into a Box and using Other impl From for std::io::Error { fn from(e: PreflateError) -> Self { return std::io::Error::new(std::io::ErrorKind::Other, e); } } -impl std::error::Error for PreflateError {} - #[test] fn test_error_translation() { // test wrapping inside an io error - fn my_std_error() -> Result<(), std::io::Error> { - Err(PreflateError::new(ExitCode::ReadDeflate, "test error").into()) + fn my_std_error() -> core::result::Result<(), std::io::Error> { + Err(PreflateError::new(ExitCode::AnalyzeFailed, "test error").into()) } let e: PreflateError = my_std_error().unwrap_err().into(); - assert_eq!(e.exit_code, ExitCode::ReadDeflate); - assert_eq!(e.message, "test error"); - - // wrapping inside anyhow - fn my_anyhow() -> Result<(), anyhow::Error> { - Err(PreflateError::new(ExitCode::ReadDeflate, "test error").into()) - } - - let e: PreflateError = my_anyhow().unwrap_err().into(); - assert_eq!(e.exit_code, ExitCode::ReadDeflate); - assert_eq!(e.message, "test error"); + assert_eq!(e.exit_code(), ExitCode::AnalyzeFailed); + assert_eq!(e.message(), "test error"); // an IO error should be translated into an OsError let e: PreflateError = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found").into(); - assert_eq!(e.exit_code, ExitCode::OsError); + assert_eq!(e.exit_code(), ExitCode::OsError); } diff --git a/src/preflate_parameter_estimator.rs b/src/preflate_parameter_estimator.rs index 1f19cdb..500bac6 100644 --- a/src/preflate_parameter_estimator.rs +++ b/src/preflate_parameter_estimator.rs @@ -4,15 +4,13 @@ * This software incorporates material from third parties. See NOTICE.txt for details. *--------------------------------------------------------------------------------------------*/ -use anyhow::Result; - use crate::{ add_policy_estimator::{estimate_add_policy, DictionaryAddPolicy}, bit_helper::bit_length, complevel_estimator::estimate_preflate_comp_level, hash_algorithm::HashAlgorithm, preflate_constants::{self}, - preflate_error::ExitCode, + preflate_error::{ExitCode, Result}, preflate_parse_config::MatchingType, preflate_stream_info::{extract_preflate_info, PreflateStreamInfo}, preflate_token::PreflateTokenBlock, @@ -302,7 +300,7 @@ pub fn estimate_preflate_huff_strategy(info: &PreflateStreamInfo) -> PreflateHuf pub fn estimate_preflate_parameters( unpacked_output: &[u8], blocks: &Vec, -) -> anyhow::Result { +) -> Result { let info = extract_preflate_info(blocks); let preflate_strategy = estimate_preflate_strategy(&info); diff --git a/src/process.rs b/src/process.rs index fa319f6..4ac9b63 100644 --- a/src/process.rs +++ b/src/process.rs @@ -57,9 +57,7 @@ pub fn parse_deflate( let mut blocks = Vec::new(); let mut last = false; while !last { - let block = block_decoder - .read_block(&mut last) - .map_err(|e| PreflateError::error_on_block(ExitCode::ReadBlock, blocks.len(), &e))?; + let block = block_decoder.read_block(&mut last)?; if deflate_info_dump_level > 0 { // Log information about this deflate compressed block @@ -95,9 +93,7 @@ fn predict_blocks( encoder.encode_misprediction(CodecMisprediction::EOFMisprediction, true); } - token_predictor_in - .predict_block(&blocks[i], encoder, i == blocks.len() - 1) - .map_err(|e| PreflateError::error_on_block(ExitCode::PredictBlock, i, &e))?; + token_predictor_in.predict_block(&blocks[i], encoder, i == blocks.len() - 1)?; if blocks[i].block_type == BlockType::DynamicHuff { predict_tree_for_block( @@ -105,8 +101,7 @@ fn predict_blocks( &blocks[i].freq, encoder, HufftreeBitCalc::Zlib, - ) - .map_err(|e| PreflateError::error_on_block(ExitCode::PredictTree, i, &e))?; + )?; } } assert!(token_predictor_in.input_eof()); @@ -144,27 +139,17 @@ fn recreate_blocks( let mut is_eof = token_predictor.input_eof() && !decoder.decode_misprediction(CodecMisprediction::EOFMisprediction); while !is_eof { - let mut block = token_predictor.recreate_block(decoder).map_err(|e| { - PreflateError::error_on_block(ExitCode::RecreateBlock, output_blocks.len(), &e) - })?; + let mut block = token_predictor.recreate_block(decoder)?; if block.block_type == BlockType::DynamicHuff { - block.huffman_encoding = recreate_tree_for_block( - &block.freq, - decoder, - HufftreeBitCalc::Zlib, - ) - .map_err(|e| { - PreflateError::error_on_block(ExitCode::RecreateTree, output_blocks.len(), &e) - })?; + block.huffman_encoding = + recreate_tree_for_block(&block.freq, decoder, HufftreeBitCalc::Zlib)?; } is_eof = token_predictor.input_eof() && !decoder.decode_misprediction(CodecMisprediction::EOFMisprediction); - deflate_writer.encode_block(&block, is_eof).map_err(|e| { - PreflateError::error_on_block(ExitCode::EncodeBlock, output_blocks.len(), &e) - })?; + deflate_writer.encode_block(&block, is_eof)?; output_blocks.push(block); } diff --git a/src/scan_deflate.rs b/src/scan_deflate.rs index 73f0166..a05a528 100644 --- a/src/scan_deflate.rs +++ b/src/scan_deflate.rs @@ -3,12 +3,13 @@ use std::io::Cursor; use crate::{ idat_parse::{parse_idat, IdatContents}, preflate_container::{decompress_deflate_stream, DecompressResult}, + preflate_error::{err_exit_code, ExitCode}, }; use byteorder::{LittleEndian, ReadBytesExt}; use std::io::{Read, Seek, SeekFrom}; -use anyhow::Result; +use crate::preflate_error::Result; /// The minimum size of a block that is considered for splitting into chunks const MIN_BLOCKSIZE: usize = 1024; @@ -160,7 +161,7 @@ fn skip_gzip_header(reader: &mut R) -> Result<()> { reader.read_exact(&mut buffer)?; // Read past the fixed 10-byte GZIP header if buffer[2] != 8 { - return Err(anyhow::Error::msg("Unsupported compression method")); + return err_exit_code(ExitCode::InvalidDeflate, "Unsupported compression method"); } if buffer[3] & 0x04 != 0 { @@ -274,7 +275,7 @@ pub struct ZipLocalFileHeader { } impl ZipLocalFileHeader { - pub fn create_and_load(binary_reader: &mut R) -> anyhow::Result { + pub fn create_and_load(binary_reader: &mut R) -> Result { let zip_local_file_header = Self { local_file_header_signature: binary_reader.read_u32::()?, version_needed_to_extract: binary_reader.read_u16::()?, @@ -294,20 +295,20 @@ impl ZipLocalFileHeader { } /// parses the zip stream and returns the size of the header, followed by the decompressed contents -fn parse_zip_stream(contents: &[u8]) -> anyhow::Result<(usize, DecompressResult)> { +fn parse_zip_stream(contents: &[u8]) -> Result<(usize, DecompressResult)> { let mut binary_reader = Cursor::new(&contents); // read the signature let zip_local_file_header = ZipLocalFileHeader::create_and_load(&mut binary_reader)?; let signature = zip_local_file_header.local_file_header_signature; if signature != ZIP_LOCAL_FILE_HEADER_SIGNATURE { - return Err(anyhow::Error::msg("No local header")); + return err_exit_code(ExitCode::InvalidDeflate, "No local header"); } // read extended information let mut file_name_buf = vec![0; zip_local_file_header.file_name_length as usize]; binary_reader.read_exact(&mut file_name_buf)?; - let _path = String::from_utf8(file_name_buf)?; + //let _path = String::from_utf8(file_name_buf).map_err( PreflateError)?; // Skip Extra field binary_reader.seek(SeekFrom::Current( @@ -323,5 +324,5 @@ fn parse_zip_stream(contents: &[u8]) -> anyhow::Result<(usize, DecompressResult) } } - Err(anyhow::Error::msg("No deflate stream found")) + err_exit_code(ExitCode::InvalidDeflate, "No deflate stream found") } diff --git a/src/token_predictor.rs b/src/token_predictor.rs index 33d6d75..53de91d 100644 --- a/src/token_predictor.rs +++ b/src/token_predictor.rs @@ -4,8 +4,6 @@ * This software incorporates material from third parties. See NOTICE.txt for details. *--------------------------------------------------------------------------------------------*/ -use anyhow::Context; - use crate::{ add_policy_estimator::DictionaryAddPolicy, bit_helper::DebugHash, @@ -13,6 +11,7 @@ use crate::{ hash_algorithm::HashAlgorithm, hash_chain_holder::{new_hash_chain_holder, HashChainHolder, MatchResult}, preflate_constants::MIN_MATCH, + preflate_error::{err_exit_code, AddContext, ExitCode, Result}, preflate_input::PreflateInput, preflate_parameter_estimator::PreflateStrategy, preflate_parse_config::MatchingType, @@ -92,7 +91,7 @@ impl<'a> TokenPredictor<'a> { block: &PreflateTokenBlock, codec: &mut D, last_block: bool, - ) -> anyhow::Result<()> { + ) -> Result<()> { self.current_token_count = 0; self.pending_reference = None; @@ -259,7 +258,7 @@ impl<'a> TokenPredictor<'a> { pub fn recreate_block( &mut self, codec: &mut D, - ) -> anyhow::Result { + ) -> Result { let mut block; self.current_token_count = 0; self.pending_reference = None; @@ -295,7 +294,7 @@ impl<'a> TokenPredictor<'a> { block = PreflateTokenBlock::new(BlockType::DynamicHuff); } _ => { - return Err(anyhow::Error::msg(format!("Invalid block type {}", bt))); + return err_exit_code(ExitCode::InvalidDeflate, "Invalid block type"); } } @@ -468,11 +467,12 @@ impl<'a> TokenPredictor<'a> { fn repredict_reference( &mut self, _dist_match: Option, - ) -> anyhow::Result { + ) -> Result { if self.input.pos() == 0 || self.input.remaining() < MIN_MATCH { - return Err(anyhow::Error::msg( + return err_exit_code( + ExitCode::RecompressFailed, "Not enough space left to find a reference", - )); + ); } /* @@ -495,10 +495,10 @@ impl<'a> TokenPredictor<'a> { } } - Err(anyhow::Error::msg(format!( - "Didnt find a match {:?}", - match_token - ))) + err_exit_code( + ExitCode::MatchNotFound, + format!("Didnt find a match {:?}", match_token).as_str(), + ) } fn commit_token(&mut self, token: &PreflateToken, block: Option<&mut PreflateTokenBlock>) { diff --git a/src/tree_predictor.rs b/src/tree_predictor.rs index 902aad0..12d9c95 100644 --- a/src/tree_predictor.rs +++ b/src/tree_predictor.rs @@ -9,6 +9,7 @@ use crate::{ huffman_calc::{calc_bit_lengths, HufftreeBitCalc}, huffman_encoding::{HuffmanOriginalEncoding, TreeCodeType}, preflate_constants::{CODETREE_CODE_COUNT, NONLEN_CODE_COUNT, TREE_CODE_ORDER_TABLE}, + preflate_error::{err_exit_code, ExitCode, Result}, preflate_token::TokenFrequency, statistical_codec::{ CodecCorrection, CodecMisprediction, PredictionDecoder, PredictionEncoder, @@ -20,7 +21,7 @@ pub fn predict_tree_for_block( freq: &TokenFrequency, encoder: &mut D, huffcalc: HufftreeBitCalc, -) -> anyhow::Result<()> { +) -> Result<()> { encoder.encode_verify_state("tree", 0); // bit_lengths is a vector of huffman code sizes for literals followed by length codes @@ -105,7 +106,7 @@ pub fn recreate_tree_for_block( freq: &TokenFrequency, codec: &mut D, huffcalc: HufftreeBitCalc, -) -> anyhow::Result { +) -> Result { codec.decode_verify_state("tree", 0); let mut result: HuffmanOriginalEncoding = Default::default(); @@ -176,7 +177,7 @@ fn predict_ld_trees( encoder: &mut D, predicted_bit_len: &[u8], actual_target_codes: &[(TreeCodeType, u8)], -) -> anyhow::Result<()> { +) -> Result<()> { let mut symbols = predicted_bit_len; let mut prev_code = None; @@ -195,7 +196,7 @@ fn predict_ld_trees( for &(target_tree_code_type, target_tree_code_data) in actual_target_codes.iter() { if symbols.is_empty() { - return Err(anyhow::anyhow!("Reconstruction failed")); + return err_exit_code(ExitCode::InvalidDeflate, "Reconstruction failed"); } let predicted_tree_code_type: TreeCodeType = predict_code_type(symbols, prev_code); @@ -243,7 +244,7 @@ fn predict_ld_trees( fn reconstruct_ld_trees( decoder: &mut D, sym_bit_len: &[u8], -) -> anyhow::Result> { +) -> Result> { let mut symbols = sym_bit_len; let mut prev_code = None; let mut result: Vec<(TreeCodeType, u8)> = Vec::new(); @@ -267,7 +268,7 @@ fn reconstruct_ld_trees( TC_REPEAT => TreeCodeType::Repeat, TC_ZERO_SHORT => TreeCodeType::ZeroShort, TC_ZERO_LONG => TreeCodeType::ZeroLong, - _ => return Err(anyhow::anyhow!("Reconstruction failed")), + _ => return err_exit_code(ExitCode::RecompressFailed, "Reconstruction failed"), }; let mut predicted_tree_code_data = predict_code_data(symbols, predicted_tree_code_type); diff --git a/tests/end_to_end.rs b/tests/end_to_end.rs index d3ab817..100ccd3 100644 --- a/tests/end_to_end.rs +++ b/tests/end_to_end.rs @@ -87,7 +87,7 @@ fn test_container(filename: &str) { #[test] fn libzng() { - let level = 1; + let level = 4; let v = read_file("pptxplaintext.bin"); println!("zlibng level: {}", level);