diff --git a/src/enabled_features.rs b/src/enabled_features.rs index fb0e3b9..81135b5 100644 --- a/src/enabled_features.rs +++ b/src/enabled_features.rs @@ -8,10 +8,10 @@ pub struct EnabledFeatures { pub reject_dqts_with_zeros: bool, /// maximum jpeg width - pub max_jpeg_width: i32, + pub max_jpeg_width: u32, // maximum jpeg height - pub max_jpeg_height: i32, + pub max_jpeg_height: u32, /// Sadly C++ version has a bug where it uses 16 bit math in the SIMD path and 32 bit math in the scalar path pub use_16bit_dc_estimate: bool, @@ -53,8 +53,8 @@ impl EnabledFeatures { Self { progressive: true, reject_dqts_with_zeros: false, - max_jpeg_height: i32::MAX, - max_jpeg_width: i32::MAX, + max_jpeg_height: u32::MAX, + max_jpeg_width: u32::MAX, use_16bit_dc_estimate: false, use_16bit_adv_predict: false, accept_invalid_dht: true, @@ -70,8 +70,8 @@ impl EnabledFeatures { Self { progressive: true, reject_dqts_with_zeros: false, - max_jpeg_height: i32::MAX, - max_jpeg_width: i32::MAX, + max_jpeg_height: u32::MAX, + max_jpeg_width: u32::MAX, use_16bit_dc_estimate: true, use_16bit_adv_predict: true, accept_invalid_dht: true, diff --git a/src/main.rs b/src/main.rs index 1128a27..140461f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -113,14 +113,14 @@ Options: override_if( &mut pargs, "--max-width", - parse_i32, + parse_u32, &mut enabled_features.max_jpeg_width, )?; override_if( &mut pargs, "--max-height", - parse_i32, + parse_u32, &mut enabled_features.max_jpeg_height, )?; diff --git a/src/structs/bit_reader.rs b/src/structs/bit_reader.rs index b221e76..08dbb63 100644 --- a/src/structs/bit_reader.rs +++ b/src/structs/bit_reader.rs @@ -23,10 +23,10 @@ pub struct BitReader { } impl BitReader { - pub fn get_stream_position(&mut self) -> i32 { + pub fn get_stream_position(&mut self) -> u32 { self.undo_read_ahead(); - let pos: i32 = (self.inner.stream_position().unwrap() - self.start_offset) + let pos: u32 = (self.inner.stream_position().unwrap() - self.start_offset) .try_into() .unwrap(); @@ -299,7 +299,7 @@ use std::io::Cursor; // test reading a simple bit pattern with an escaped 0xff inside it. #[test] fn read_simple() { - let arr = [0x12 as u8, 0x34, 0x45, 0x67, 0x89, 0xff, 00, 0xee]; + let arr = [0x12u8, 0x34, 0x45, 0x67, 0x89, 0xff, 00, 0xee]; let mut b = BitReader::new(Cursor::new(&arr)); @@ -338,7 +338,7 @@ fn read_simple() { // what happens when a file has 0xff as the last character (assume that it is an escaped 0xff) #[test] fn read_truncate_ff() { - let arr = [0x12 as u8, 0xff]; + let arr = [0x12u8, 0xff]; let mut b = BitReader::new(Cursor::new(&arr)); diff --git a/src/structs/bit_writer.rs b/src/structs/bit_writer.rs index 57cefc4..379c26e 100644 --- a/src/structs/bit_writer.rs +++ b/src/structs/bit_writer.rs @@ -151,7 +151,7 @@ use crate::structs::bit_reader::BitReader; // write a test pattern with an escape and see if it matches #[test] fn write_simple() { - let arr = [0x12 as u8, 0x34, 0x45, 0x67, 0x89, 0xff, 00, 0xee]; + let arr = [0x12, 0x34, 0x45, 0x67, 0x89, 0xff, 00, 0xee]; let mut b = BitWriter::new(1024); @@ -178,7 +178,7 @@ fn roundtrip_bits() { { let mut b = BitWriter::new(1024); for i in 1..2048 { - b.write(i, u32_bit_length(i as u32) as u32); + b.write(i, u32_bit_length(i) as u32); } b.pad(0xff); diff --git a/src/structs/block_based_image.rs b/src/structs/block_based_image.rs index 0e908cc..eb79f23 100644 --- a/src/structs/block_based_image.rs +++ b/src/structs/block_based_image.rs @@ -16,11 +16,11 @@ use crate::structs::jpeg_header::JPegHeader; /// the image may only hold a subset of the components (specified by dpos_offset), /// but they can be merged pub struct BlockBasedImage { - block_width: i32, + block_width: u32, - original_height: i32, + original_height: u32, - dpos_offset: i32, + dpos_offset: u32, image: Vec, } @@ -32,22 +32,22 @@ impl BlockBasedImage { pub fn new( jpeg_header: &JPegHeader, component: usize, - luma_y_start: i32, - luma_y_end: i32, + luma_y_start: u32, + luma_y_end: u32, ) -> Self { let block_width = jpeg_header.cmp_info[component].bch; let original_height = jpeg_header.cmp_info[component].bcv; let max_size = block_width * original_height; let image_capcity = usize::try_from( - (i64::from(max_size) * i64::from(luma_y_end - luma_y_start) - + i64::from(jpeg_header.cmp_info[0].bcv - 1 /* round up */)) - / i64::from(jpeg_header.cmp_info[0].bcv), + (u64::from(max_size) * u64::from(luma_y_end - luma_y_start) + + u64::from(jpeg_header.cmp_info[0].bcv - 1 /* round up */)) + / u64::from(jpeg_header.cmp_info[0].bcv), ) .unwrap(); - let dpos_offset = i32::try_from( - i64::from(max_size) * i64::from(luma_y_start) / i64::from(jpeg_header.cmp_info[0].bcv), + let dpos_offset = u32::try_from( + u64::from(max_size) * u64::from(luma_y_start) / u64::from(jpeg_header.cmp_info[0].bcv), ) .unwrap(); @@ -70,7 +70,7 @@ impl BlockBasedImage { for v in images { assert!( - v[index].dpos_offset == contents.len() as i32, + v[index].dpos_offset == contents.len() as u32, "previous content should match new content" ); @@ -111,20 +111,20 @@ impl BlockBasedImage { } // blocks above the first line are never dereferenced - pub fn off_y(&self, y: i32) -> BlockContext { + pub fn off_y(&self, y: u32) -> BlockContext { return BlockContext::new( self.block_width * y, - self.block_width * (y - 1), + if y > 0 { self.block_width * (y - 1) } else { 0 }, if (y & 1) != 0 { self.block_width } else { 0 }, if (y & 1) != 0 { 0 } else { self.block_width }, ); } - pub fn get_block_width(&self) -> i32 { + pub fn get_block_width(&self) -> u32 { self.block_width } - pub fn get_original_height(&self) -> i32 { + pub fn get_original_height(&self) -> u32 { self.original_height } @@ -133,7 +133,7 @@ impl BlockBasedImage { #[inline(always)] pub fn fill_up_to_dpos( &mut self, - dpos: i32, + dpos: u32, block_to_write: Option, ) -> &mut AlignedBlock { // ensure that dpos_offset got set to the right value when we start writing @@ -149,7 +149,7 @@ impl BlockBasedImage { if relative_offset < self.image.len() { // rewrite already written block if let Some(b) = block_to_write { - self.image[relative_offset as usize] = b; + self.image[relative_offset] = b; } } else { // need to extend the image length and add any necessary @@ -166,14 +166,14 @@ impl BlockBasedImage { self.image.push(block_to_write.unwrap_or_default()); } - return &mut self.image[relative_offset as usize]; + return &mut self.image[relative_offset]; } - pub fn set_block_data(&mut self, dpos: i32, block_data: AlignedBlock) { + pub fn set_block_data(&mut self, dpos: u32, block_data: AlignedBlock) { self.fill_up_to_dpos(dpos, Some(block_data)); } - pub fn get_block(&self, dpos: i32) -> &AlignedBlock { + pub fn get_block(&self, dpos: u32) -> &AlignedBlock { if (dpos - self.dpos_offset) as usize >= self.image.len() { return &EMPTY; } else { @@ -191,7 +191,7 @@ impl BlockBasedImage { } #[inline(always)] - pub fn get_block_mut(&mut self, dpos: i32) -> &mut AlignedBlock { + pub fn get_block_mut(&mut self, dpos: u32) -> &mut AlignedBlock { self.fill_up_to_dpos(dpos, None) } } diff --git a/src/structs/block_context.rs b/src/structs/block_context.rs index 5a83dbb..c2c2ba8 100644 --- a/src/structs/block_context.rs +++ b/src/structs/block_context.rs @@ -8,11 +8,11 @@ use crate::structs::block_based_image::{AlignedBlock, BlockBasedImage, EMPTY_BLO use crate::structs::neighbor_summary::{NeighborSummary, NEIGHBOR_DATA_EMPTY}; use crate::structs::probability_tables::ProbabilityTables; pub struct BlockContext { - cur_block_index: i32, - above_block_index: i32, + cur_block_index: u32, + above_block_index: u32, - cur_neighbor_summary_index: i32, - above_neighbor_summary_index: i32, + cur_neighbor_summary_index: u32, + above_neighbor_summary_index: u32, } pub struct NeighborData<'a> { pub above: &'a AlignedBlock, @@ -25,13 +25,13 @@ pub struct NeighborData<'a> { impl BlockContext { // for debugging #[allow(dead_code)] - pub fn get_here_index(&self) -> i32 { + pub fn get_here_index(&self) -> u32 { self.cur_block_index } // as each new line BlockContext is set by `off_y`, no edge cases with dereferencing // out of bounds indices is possilbe, therefore no special treatment is needed - pub fn next(&mut self) -> i32 { + pub fn next(&mut self) -> u32 { self.cur_block_index += 1; self.above_block_index += 1; self.cur_neighbor_summary_index += 1; @@ -41,10 +41,10 @@ impl BlockContext { } pub fn new( - cur_block_index: i32, - above_block_index: i32, - cur_neighbor_summary_index: i32, - above_neighbor_summary_index: i32, + cur_block_index: u32, + above_block_index: u32, + cur_neighbor_summary_index: u32, + above_neighbor_summary_index: u32, ) -> Self { return BlockContext { cur_block_index, diff --git a/src/structs/branch.rs b/src/structs/branch.rs index 965c9e4..03de87c 100644 --- a/src/structs/branch.rs +++ b/src/structs/branch.rs @@ -231,7 +231,7 @@ fn test_all_probabilities() { continue; } - let mut new_f = Branch { counts: i as u16 }; + let mut new_f = Branch { counts: i }; for _k in 0..10 { old_f.record_obs_and_update(false); diff --git a/src/structs/component_info.rs b/src/structs/component_info.rs index 7c6aba8..ba104cf 100644 --- a/src/structs/component_info.rs +++ b/src/structs/component_info.rs @@ -16,34 +16,34 @@ pub struct ComponentInfo { pub huff_ac: u8, /// sample factor vertical - pub sfv: i32, + pub sfv: u32, /// sample factor horizontal - pub sfh: i32, + pub sfh: u32, /// blocks in mcu - pub mbs: i32, + pub mbs: u32, /// block count vertical (interleaved) - pub bcv: i32, + pub bcv: u32, /// block count horizontal (interleaved) - pub bch: i32, + pub bch: u32, /// block count (all) (interleaved) - pub bc: i32, + pub bc: u32, /// block count vertical (non interleaved) - pub ncv: i32, + pub ncv: u32, /// block count horizontal (non interleaved) - pub nch: i32, + pub nch: u32, /// block count (all) (non interleaved) - pub nc: i32, + pub nc: u32, /// statistical identity - pub sid: i32, + pub sid: u32, /// jpeg internal id pub jid: u8, @@ -53,16 +53,16 @@ impl Default for ComponentInfo { fn default() -> ComponentInfo { return ComponentInfo { q_table_index: 0xff, - sfv: -1, - sfh: -1, - mbs: -1, - bcv: -1, - bch: -1, - bc: -1, - ncv: -1, - nch: -1, - nc: -1, - sid: -1, + sfv: u32::MAX, + sfh: u32::MAX, + mbs: u32::MAX, + bcv: u32::MAX, + bch: u32::MAX, + bc: u32::MAX, + ncv: u32::MAX, + nch: u32::MAX, + nc: u32::MAX, + sid: u32::MAX, jid: 0xff, huff_dc: 0xff, huff_ac: 0xff, diff --git a/src/structs/jpeg_header.rs b/src/structs/jpeg_header.rs index 1be2d73..a8e8571 100644 --- a/src/structs/jpeg_header.rs +++ b/src/structs/jpeg_header.rs @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ use std::io::Read; +use std::num::NonZeroU32; use crate::consts::JPegType; use crate::enabled_features::EnabledFeatures; @@ -43,6 +44,7 @@ use crate::structs::component_info::ComponentInfo; use crate::structs::lepton_header::LeptonHeader; use crate::structs::quantization_tables::QuantizationTables; use crate::structs::truncate_components::TruncateComponents; +use crate::LeptonError; #[derive(Copy, Clone, Debug)] pub struct HuffCodes { @@ -125,10 +127,10 @@ impl HuffCodes { for i in 0..256 { let s = i & 0xf; self.c_len_plus_s[i] = (self.c_len[i] + (s as u16)) as u8; - self.c_val_shift_s[i] = (self.c_val[i] as u32) << s; + self.c_val_shift_s[i] = u32::from(self.c_val[i]) << s; // calculate the value for negative coefficients, which compensates for the sign bit - self.c_val_shift_s[i + 256] = ((self.c_val[i] as u32) << s) | ((1u32 << s) - 1); + self.c_val_shift_s[i + 256] = (u32::from(self.c_val[i]) << s) | ((1u32 << s) - 1); } // find out eobrun (runs of all zero blocks) max value. This is used encoding/decoding progressive files. @@ -267,31 +269,68 @@ impl HuffTree { #[derive(Debug, Clone)] pub struct JPegHeader { - pub q_tables: [[u16; 64]; 4], // quantization tables 4 x 64 - h_codes: [[HuffCodes; 4]; 2], // huffman codes (access via get_huff_xx_codes) - h_trees: [[HuffTree; 4]; 2], // huffman decoding trees (access via get_huff_xx_tree) - pub ht_set: [[u8; 4]; 2], // 1 if huffman table is set - pub cmp_info: [ComponentInfo; 4], // components - pub cmpc: usize, // component count - pub img_width: i32, // width of image - pub img_height: i32, // height of image + /// quantization tables 4 x 64 + pub q_tables: [[u16; 64]; 4], + + /// huffman codes (access via get_huff_xx_codes) + h_codes: [[HuffCodes; 4]; 2], + + /// huffman decoding trees (access via get_huff_xx_tree) + h_trees: [[HuffTree; 4]; 2], + + /// 1 if huffman table is set + pub ht_set: [[u8; 4]; 2], + + /// components + pub cmp_info: [ComponentInfo; 4], + + /// component count + pub cmpc: usize, + + /// width of image + pub img_width: u32, + + /// height of image + pub img_height: u32, pub jpeg_type: JPegType, - pub sfhm: i32, // max horizontal sample factor - pub sfvm: i32, // max verical sample factor - pub mcuv: i32, // mcus per line - pub mcuh: i32, // mcus per collumn - pub mcuc: i32, // count of mcus - pub rsti: i32, // restart interval - pub cs_cmpc: usize, // component count in current scan - pub cs_cmp: [usize; 4], // component numbers in current scan + /// max horizontal sample factor + pub sfhm: u32, + + /// max verical sample factor + pub sfvm: u32, + + // mcus per line + pub mcuv: NonZeroU32, + + /// mcus per column + pub mcuh: NonZeroU32, + + /// count of mcus + pub mcuc: u32, + + /// restart interval + pub rsti: u32, + + /// component count in current scan + pub cs_cmpc: usize, + + /// component numbers in current scan + pub cs_cmp: [usize; 4], // variables: info about current scan - pub cs_from: u8, // begin - band of current scan ( inclusive ) - pub cs_to: u8, // end - band of current scan ( inclusive ) - pub cs_sah: u8, // successive approximation bit pos high - pub cs_sal: u8, // successive approximation bit pos low + /// begin - band of current scan ( inclusive ) + pub cs_from: u8, + + /// end - band of current scan ( inclusive ) + pub cs_to: u8, + + /// successive approximation bit pos high + pub cs_sah: u8, + + /// successive approximation bit pos low + pub cs_sal: u8, } pub struct JPegEncodingInfo { @@ -300,7 +339,7 @@ pub struct JPegEncodingInfo { /// A list containing one entry for each scan segment. Each entry contains the number of restart intervals /// within the corresponding scan segment. - pub rst_cnt: Vec, + pub rst_cnt: Vec, /// the mask for padding out the bitstream when we get to the end of a reset block pub pad_bit: Option, @@ -349,8 +388,8 @@ impl Default for JPegHeader { jpeg_type: JPegType::Unknown, sfhm: 0, sfvm: 0, - mcuv: 0, - mcuh: 0, + mcuv: NonZeroU32::MIN, + mcuh: NonZeroU32::MIN, mcuc: 0, rsti: 0, cs_cmpc: 0, @@ -364,18 +403,22 @@ impl Default for JPegHeader { } impl JPegHeader { + #[inline(always)] pub fn get_huff_dc_codes(&self, cmp: usize) -> &HuffCodes { &self.h_codes[0][usize::from(self.cmp_info[cmp].huff_dc)] } + #[inline(always)] pub fn get_huff_dc_tree(&self, cmp: usize) -> &HuffTree { &self.h_trees[0][usize::from(self.cmp_info[cmp].huff_dc)] } + #[inline(always)] pub fn get_huff_ac_codes(&self, cmp: usize) -> &HuffCodes { &self.h_codes[1][usize::from(self.cmp_info[cmp].huff_ac)] } + #[inline(always)] pub fn get_huff_ac_tree(&self, cmp: usize) -> &HuffTree { &self.h_trees[1][usize::from(self.cmp_info[cmp].huff_ac)] } @@ -434,30 +477,37 @@ impl JPegHeader { } } - self.mcuv = (1.0 * self.img_height as f64 / (8.0 * self.sfhm as f64)).ceil() as i32; - self.mcuh = (1.0 * self.img_width as f64 / (8.0 * self.sfvm as f64)).ceil() as i32; - self.mcuc = self.mcuv * self.mcuh; + self.mcuv = NonZeroU32::new( + (1.0 * self.img_height as f64 / (8.0 * self.sfhm as f64)).ceil() as u32, + ) + .ok_or_else(|| LeptonError::new(ExitCode::UnsupportedJpeg, "mcuv is zero"))?; + + self.mcuh = + NonZeroU32::new((1.0 * self.img_width as f64 / (8.0 * self.sfvm as f64)).ceil() as u32) + .ok_or_else(|| LeptonError::new(ExitCode::UnsupportedJpeg, "mcuh is zero"))?; + + self.mcuc = self.mcuv.get() * self.mcuh.get(); for cmp in 0..self.cmpc { self.cmp_info[cmp].mbs = self.cmp_info[cmp].sfv * self.cmp_info[cmp].sfh; - self.cmp_info[cmp].bcv = self.mcuv * self.cmp_info[cmp].sfh; - self.cmp_info[cmp].bch = self.mcuh * self.cmp_info[cmp].sfv; + self.cmp_info[cmp].bcv = self.mcuv.get() * self.cmp_info[cmp].sfh; + self.cmp_info[cmp].bch = self.mcuh.get() * self.cmp_info[cmp].sfv; self.cmp_info[cmp].bc = self.cmp_info[cmp].bcv * self.cmp_info[cmp].bch; self.cmp_info[cmp].ncv = (1.0 * self.img_height as f64 * (self.cmp_info[cmp].sfh as f64 / (8.0 * self.sfhm as f64))) - .ceil() as i32; + .ceil() as u32; self.cmp_info[cmp].nch = (1.0 * self.img_width as f64 * (self.cmp_info[cmp].sfv as f64 / (8.0 * self.sfvm as f64))) - .ceil() as i32; + .ceil() as u32; self.cmp_info[cmp].nc = self.cmp_info[cmp].ncv * self.cmp_info[cmp].nch; } // decide components' statistical ids if self.cmpc <= 3 { for cmp in 0..self.cmpc { - self.cmp_info[cmp].sid = cmp as i32; + self.cmp_info[cmp].sid = cmp as u32; } } else { for cmp in 0..self.cmpc { @@ -642,7 +692,7 @@ impl JPegHeader { { // DRI segment // define restart interval ensure_space(segment,hpos, 2).context()?; - self.rsti = b_short(segment[hpos], segment[hpos + 1]) as i32; + self.rsti = u32::from(b_short(segment[hpos], segment[hpos + 1])); } jpeg_code::SOS => // SOS segment @@ -741,8 +791,8 @@ impl JPegHeader { } // image size, height & component count - self.img_height = i32::from(b_short(segment[hpos + 1], segment[hpos + 2])); - self.img_width = i32::from(b_short(segment[hpos + 3], segment[hpos + 4])); + self.img_height = u32::from(b_short(segment[hpos + 1], segment[hpos + 2])); + self.img_width = u32::from(b_short(segment[hpos + 3], segment[hpos + 4])); if self.img_height == 0 || self.img_width == 0 { @@ -769,8 +819,8 @@ impl JPegHeader { ensure_space(segment,hpos, 3).context()?; self.cmp_info[cmp].jid = segment[hpos]; - self.cmp_info[cmp].sfv = lbits(segment[hpos + 1], 4) as i32; - self.cmp_info[cmp].sfh = rbits(segment[hpos + 1], 4) as i32; + self.cmp_info[cmp].sfv = u32::from(lbits(segment[hpos + 1], 4)); + self.cmp_info[cmp].sfh = u32::from(rbits(segment[hpos + 1], 4)); if self.cmp_info[cmp].sfv > 2 || self.cmp_info[cmp].sfh > 2 { @@ -994,12 +1044,7 @@ pub fn generate_huff_table_from_distribution(freq: &[usize; 256]) -> HuffCodes { pq.pop().unwrap() } - fn generate_codes( - root: &Node, - codes: &mut std::collections::HashMap, - prefix: u16, - length: u8, - ) { + fn generate_codes(root: &Node, codes: &mut HashMap, prefix: u16, length: u8) { if let Some(symbol) = root.symbol { codes.insert(symbol, (prefix, length)); } else { diff --git a/src/structs/jpeg_position_state.rs b/src/structs/jpeg_position_state.rs index 283aaee..9e09a91 100644 --- a/src/structs/jpeg_position_state.rs +++ b/src/structs/jpeg_position_state.rs @@ -16,19 +16,19 @@ pub struct JpegPositionState { cmp: usize, /// current minimum coded unit (a fraction of dpos) - mcu: i32, + mcu: u32, /// index of component csc: usize, /// offset within mcu - sub: i32, + sub: u32, /// current block position in image for this component - dpos: i32, + dpos: u32, /// number of blocks left until reset interval - rstw: i32, + rstw: u32, /// tracks long zero byte runs in progressive images pub eobrun: u16, @@ -40,7 +40,7 @@ pub struct JpegPositionState { } impl JpegPositionState { - pub fn new(jf: &JPegHeader, mcu: i32) -> Self { + pub fn new(jf: &JPegHeader, mcu: u32) -> Self { let cmp = jf.cs_cmp[0]; let mcumul = jf.cmp_info[cmp].sfv * jf.cmp_info[cmp].sfh; @@ -61,17 +61,17 @@ impl JpegPositionState { return state; } - pub fn get_mcu(&self) -> i32 { + pub fn get_mcu(&self) -> u32 { self.mcu } - pub fn get_dpos(&self) -> i32 { + pub fn get_dpos(&self) -> u32 { self.dpos } pub fn get_cmp(&self) -> usize { self.cmp } - pub fn get_cumulative_reset_markers(&self, jf: &JPegHeader) -> i32 { + pub fn get_cumulative_reset_markers(&self, jf: &JPegHeader) -> u32 { if self.rstw != 0 { self.get_mcu() / jf.rsti } else { @@ -129,7 +129,7 @@ impl JpegPositionState { } let mut sta = JPegDecodeStatus::DecodeInProgress; // status - let local_mcuh = jf.mcuh; + let local_mcuh = jf.mcuh.get(); let mut local_mcu = self.mcu; let mut local_cmp = self.cmp; @@ -202,18 +202,18 @@ impl JpegPositionState { // compare rst wait counter if needed if jf.rsti > 0 { - if i32::from(self.eobrun) > self.rstw { + if u32::from(self.eobrun) > self.rstw { return err_exit_code( ExitCode::UnsupportedJpeg, "skip_eobrun: eob run extends passed end of reset interval", ) .context(); } else { - self.rstw -= i32::from(self.eobrun); + self.rstw -= u32::from(self.eobrun); } } - fn checked_add(a: i32, b: i32) -> Result { + fn checked_add(a: u32, b: u32) -> Result { a.checked_add(b) .ok_or_else(|| LeptonError::new(ExitCode::UnsupportedJpeg, "integer overflow")) } @@ -224,7 +224,7 @@ impl JpegPositionState { if cmp_info.bch != cmp_info.nch { self.dpos = checked_add( self.dpos, - (((self.dpos % cmp_info.bch) + i32::from(self.eobrun)) / cmp_info.nch) + (((self.dpos % cmp_info.bch) + u32::from(self.eobrun)) / cmp_info.nch) * (cmp_info.bch - cmp_info.nch), ) .context()?; @@ -237,7 +237,7 @@ impl JpegPositionState { } // skip blocks - self.dpos = checked_add(self.dpos, i32::from(self.eobrun)).context()?; + self.dpos = checked_add(self.dpos, u32::from(self.eobrun)).context()?; // reset eobrun self.eobrun = 0; diff --git a/src/structs/jpeg_read.rs b/src/structs/jpeg_read.rs index 5908997..817f091 100644 --- a/src/structs/jpeg_read.rs +++ b/src/structs/jpeg_read.rs @@ -616,7 +616,7 @@ fn decode_ac_prg_fs( } else { // decode eobrun let s = l; - let n = bit_reader.read(u32::from(s))? as u16; + let n = bit_reader.read(u32::from(s))?; state.eobrun = decode_eobrun_bits(s, n); state.eobrun -= 1; // decrement eobrun ( for this one ) @@ -694,7 +694,7 @@ fn decode_ac_prg_sa( // decode eobrun eob = bpos; let s = l; - let n = bit_reader.read(u32::from(s))? as u16; + let n = bit_reader.read(u32::from(s))?; state.eobrun = decode_eobrun_bits(s, n); // since we hit EOB, the rest can be done with the zero block decoder diff --git a/src/structs/jpeg_write.rs b/src/structs/jpeg_write.rs index e3f2352..d82b781 100644 --- a/src/structs/jpeg_write.rs +++ b/src/structs/jpeg_write.rs @@ -51,8 +51,8 @@ pub fn jpeg_write_baseline_row_range( encoded_length: usize, overhang_byte: u8, num_overhang_bits: u8, - luma_y_start: i32, - luma_y_end: i32, + luma_y_start: u32, + luma_y_end: u32, mut last_dc: [i16; 4], image_data: &[BlockBasedImage], jenc: &JPegEncodingInfo, @@ -92,7 +92,7 @@ pub fn jpeg_write_baseline_row_range( if cur_row.last_row_to_complete_mcu { recode_one_mcu_row( &mut huffw, - cur_row.mcu_row_index * jenc.jpeg_header.mcuh, + cur_row.mcu_row_index * jenc.jpeg_header.mcuh.get(), &mut last_dc, image_data, jenc, @@ -138,7 +138,7 @@ pub fn jpeg_write_entire_scan( if cur_row.last_row_to_complete_mcu { let r = recode_one_mcu_row( &mut huffw, - cur_row.mcu_row_index * jenc.jpeg_header.mcuh, + cur_row.mcu_row_index * jenc.jpeg_header.mcuh.get(), &mut last_dc, image_data, jenc, @@ -157,7 +157,7 @@ pub fn jpeg_write_entire_scan( #[inline(never)] fn recode_one_mcu_row( huffw: &mut BitWriter, - mcu: i32, + mcu: u32, lastdc: &mut [i16], framebuffer: &[BlockBasedImage], jenc: &JPegEncodingInfo, diff --git a/src/structs/lepton_decoder.rs b/src/structs/lepton_decoder.rs index ddf23f1..792993f 100644 --- a/src/structs/lepton_decoder.rs +++ b/src/structs/lepton_decoder.rs @@ -35,8 +35,8 @@ pub fn lepton_decode_row_range( trunc: &TruncateComponents, image_data: &mut [BlockBasedImage], reader: &mut R, - min_y: i32, - max_y: i32, + min_y: u32, + max_y: u32, is_last_thread: bool, full_file_compression: bool, features: &EnabledFeatures, @@ -131,8 +131,8 @@ fn decode_row_wrapper( image_data: &mut BlockBasedImage, qt: &QuantizationTables, neighbor_summary_cache: &mut [NeighborSummary], - curr_y: i32, - component_size_in_blocks: i32, + curr_y: u32, + component_size_in_blocks: u32, features: &EnabledFeatures, ) -> Result<()> { let mut block_context = image_data.off_y(curr_y); @@ -458,7 +458,7 @@ fn decode_one_edge( if coef != 0 { num_non_zeros_edge -= 1; here_mut.set_coefficient(coord_tr, coef); - raster[coord_tr as usize] = + raster[coord_tr] = i32::from(coef) * i32::from(qt.get_quantization_table_transposed()[coord_tr]); } diff --git a/src/structs/lepton_encoder.rs b/src/structs/lepton_encoder.rs index 82b316d..82225ec 100644 --- a/src/structs/lepton_encoder.rs +++ b/src/structs/lepton_encoder.rs @@ -34,8 +34,8 @@ pub fn lepton_encode_row_range( writer: &mut W, _thread_id: i32, colldata: &TruncateComponents, - min_y: i32, - max_y: i32, + min_y: u32, + max_y: u32, is_last_thread: bool, full_file_compression: bool, features: &EnabledFeatures, @@ -155,8 +155,8 @@ fn process_row( image_data: &BlockBasedImage, qt: &QuantizationTables, neighbor_summary_cache: &mut [NeighborSummary], - curr_y: i32, - component_size_in_block: i32, + curr_y: u32, + component_size_in_block: u32, features: &EnabledFeatures, ) -> Result<()> { let mut block_context = image_data.off_y(curr_y); diff --git a/src/structs/lepton_file_reader.rs b/src/structs/lepton_file_reader.rs index 5257273..12d2824 100644 --- a/src/structs/lepton_file_reader.rs +++ b/src/structs/lepton_file_reader.rs @@ -329,13 +329,13 @@ impl LeptonFileReader { let mut markers = Vec::new(); let cumulative_reset_markers = if lh.jpeg_header.rsti != 0 { - ((lh.jpeg_header.mcuh * lh.jpeg_header.mcuv) - 1) / lh.jpeg_header.rsti + (lh.jpeg_header.mcuc - 1) / lh.jpeg_header.rsti } else { 0 } as u8; - for i in 0..lh.rst_err[0] as u8 { - let rst = (jpeg_code::RST0 + ((cumulative_reset_markers + i) & 7)) as u8; + for i in 0..lh.rst_err[0] { + let rst = jpeg_code::RST0 + ((cumulative_reset_markers + i) & 7); markers.push(0xFF); markers.push(rst); } diff --git a/src/structs/lepton_file_writer.rs b/src/structs/lepton_file_writer.rs index 8499c51..06b1569 100644 --- a/src/structs/lepton_file_writer.rs +++ b/src/structs/lepton_file_writer.rs @@ -162,11 +162,11 @@ pub fn read_jpeg( } let mut thread_handoff = Vec::::new(); - let start_scan = reader.stream_position()? as i32; + let start_scan: u32 = reader.stream_position()?.try_into().unwrap(); read_scan(&mut lp, reader, &mut thread_handoff, &mut image_data[..]).context()?; lp.scnc += 1; - let mut end_scan = reader.stream_position()? as i32; + let mut end_scan = reader.stream_position()?.try_into().unwrap(); // need at least two bytes of scan data if start_scan + 2 > end_scan || thread_handoff.len() == 0 { @@ -207,7 +207,8 @@ pub fn read_jpeg( // and then fix up the broken file later in the decoder. The following logic will create a valid file // that the C++ and CS version will still decode properly without the fixup logic. let len = thread_handoff.len(); - thread_handoff[len - 1].segment_size -= 2; + thread_handoff[len - 1].segment_size = + thread_handoff[len - 1].segment_size.saturating_sub(2); } // rest of data is garbage data if it is a sequential jpeg (including EOI marker) @@ -239,7 +240,7 @@ pub fn read_jpeg( } } - end_scan = reader.stream_position()? as i32; + end_scan = reader.stream_position()? as u32; // since prepare_to_decode_next_scan consumes the EOI, // we need to add it to the beginning of the garbage data (if there is any) @@ -252,7 +253,7 @@ pub fn read_jpeg( } } - set_segment_size_in_row_thread_handoffs(&mut thread_handoff[..], end_scan as i32); + set_segment_size_in_row_thread_handoffs(&mut thread_handoff[..], end_scan); let merged_handoffs = split_row_handoffs_to_threads(&thread_handoff[..], enabled_features.max_threads as usize); lp.thread_handoff = merged_handoffs; @@ -379,7 +380,7 @@ fn split_row_handoffs_to_threads( info!("Number of threads: {0}", num_threads); - let mut selected_splits = Vec::with_capacity(num_threads as usize); + let mut selected_splits = Vec::with_capacity(num_threads); if num_threads == 1 { // Single thread execution - no split, run on the whole range @@ -450,7 +451,7 @@ fn prepare_to_decode_next_scan( return Ok(false); } - lp.max_bpos = cmp::max(lp.max_bpos, lp.jpeg_header.cs_to as i32); + lp.max_bpos = cmp::max(lp.max_bpos, u32::from(lp.jpeg_header.cs_to)); // FIXME: not sure why only first bit of csSah is examined but 4 bits of it are stored lp.max_sah = cmp::max( @@ -459,7 +460,7 @@ fn prepare_to_decode_next_scan( ); for i in 0..lp.jpeg_header.cs_cmpc { - lp.max_cmp = cmp::max(lp.max_cmp, lp.jpeg_header.cs_cmp[i] as i32); + lp.max_cmp = cmp::max(lp.max_cmp, lp.jpeg_header.cs_cmp[i] as u32); } return Ok(true); @@ -467,7 +468,7 @@ fn prepare_to_decode_next_scan( fn set_segment_size_in_row_thread_handoffs( thread_handoffs: &mut [ThreadHandoff], - entropy_data_end_offset_in_file: i32, + entropy_data_end_offset_in_file: u32, ) { if thread_handoffs.len() != 0 { for i in 0..thread_handoffs.len() - 1 { diff --git a/src/structs/lepton_header.rs b/src/structs/lepton_header.rs index 4edf85d..aae6c28 100644 --- a/src/structs/lepton_header.rs +++ b/src/structs/lepton_header.rs @@ -39,7 +39,7 @@ pub struct LeptonHeader { /// A list containing one entry for each scan segment. Each entry contains the number of restart intervals /// within the corresponding scan segment. - pub rst_cnt: Vec, + pub rst_cnt: Vec, /// the mask for padding out the bitstream when we get to the end of a reset block pub pad_bit: Option, @@ -55,13 +55,13 @@ pub struct LeptonHeader { pub early_eof_encountered: bool, /// the maximum dpos in a truncated image - pub max_dpos: [i32; 4], + pub max_dpos: [u32; 4], /// the maximum component in a truncated image - pub max_cmp: i32, + pub max_cmp: u32, /// the maximum band in a truncated image - pub max_bpos: i32, + pub max_bpos: u32, /// the maximum bit in a truncated image pub max_sah: u8, @@ -193,10 +193,10 @@ impl LeptonHeader { // if the last segment was too big to fit with the garbage data taken into account, shorten it // (a bit of broken logic in the encoder, but can't change it without breaking the file format) if self.early_eof_encountered { - let mut max_last_segment_size = i32::try_from(self.jpeg_file_size)? - - i32::try_from(self.garbage_data.len())? - - i32::try_from(self.raw_jpeg_header_read_index)? - - SOI.len() as i32; + let mut max_last_segment_size = self.jpeg_file_size + - u32::try_from(self.garbage_data.len())? + - u32::try_from(self.raw_jpeg_header_read_index)? + - u32::try_from(SOI.len())?; // subtract the segment sizes of all the previous segments (except for the last) for i in 0..num_threads - 1 { @@ -260,7 +260,7 @@ impl LeptonHeader { // beginning here: recovery information (needed for exact JPEG recovery) // read further recovery information if any loop { - let mut current_lepton_marker = [0 as u8; 3]; + let mut current_lepton_marker = [0u8; 3]; match header_reader.read_exact(&mut current_lepton_marker) { Ok(_) => {} Err(e) => { @@ -283,7 +283,7 @@ impl LeptonHeader { let rst_count = header_reader.read_u32::()?; for _i in 0..rst_count { - self.rst_cnt.push(header_reader.read_i32::()?); + self.rst_cnt.push(header_reader.read_u32::()?); } } else if buffer_prefix_matches_marker( current_lepton_marker, @@ -325,13 +325,13 @@ impl LeptonHeader { current_lepton_marker, LEPTON_HEADER_EARLY_EOF_MARKER, ) { - self.max_cmp = header_reader.read_i32::()?; - self.max_bpos = header_reader.read_i32::()?; - self.max_sah = u8::try_from(header_reader.read_i32::()?)?; - self.max_dpos[0] = header_reader.read_i32::()?; - self.max_dpos[1] = header_reader.read_i32::()?; - self.max_dpos[2] = header_reader.read_i32::()?; - self.max_dpos[3] = header_reader.read_i32::()?; + self.max_cmp = header_reader.read_u32::()?; + self.max_bpos = header_reader.read_u32::()?; + self.max_sah = u8::try_from(header_reader.read_u32::()?)?; + self.max_dpos[0] = header_reader.read_u32::()?; + self.max_dpos[1] = header_reader.read_u32::()?; + self.max_dpos[2] = header_reader.read_u32::()?; + self.max_dpos[3] = header_reader.read_u32::()?; self.early_eof_encountered = true; } else { return err_exit_code(ExitCode::BadLeptonFile, "unknown data found"); @@ -466,7 +466,7 @@ impl LeptonHeader { mrw.write_u32::(self.rst_cnt.len() as u32)?; for i in 0..self.rst_cnt.len() { - mrw.write_u32::(self.rst_cnt[i] as u32)?; + mrw.write_u32::(self.rst_cnt[i])?; } } @@ -495,13 +495,13 @@ impl LeptonHeader { // EEE marker mrw.write_all(&LEPTON_HEADER_EARLY_EOF_MARKER)?; - mrw.write_i32::(self.max_cmp)?; - mrw.write_i32::(self.max_bpos)?; - mrw.write_i32::(i32::from(self.max_sah))?; - mrw.write_i32::(self.max_dpos[0])?; - mrw.write_i32::(self.max_dpos[1])?; - mrw.write_i32::(self.max_dpos[2])?; - mrw.write_i32::(self.max_dpos[3])?; + mrw.write_u32::(self.max_cmp)?; + mrw.write_u32::(self.max_bpos)?; + mrw.write_u32::(u32::from(self.max_sah))?; + mrw.write_u32::(self.max_dpos[0])?; + mrw.write_u32::(self.max_dpos[1])?; + mrw.write_u32::(self.max_dpos[2])?; + mrw.write_u32::(self.max_dpos[3])?; } Ok(()) diff --git a/src/structs/mod.rs b/src/structs/mod.rs index fc423e3..4bef598 100644 --- a/src/structs/mod.rs +++ b/src/structs/mod.rs @@ -7,6 +7,7 @@ // Don't allow any unsafe code by default. Since this code has to potentially deal with // badly/maliciously formatted images, we want this extra level of safety. #![forbid(unsafe_code)] +#![forbid(trivial_numeric_casts)] mod bit_reader; mod bit_writer; diff --git a/src/structs/quantization_tables.rs b/src/structs/quantization_tables.rs index 8c2e1a4..32844cb 100644 --- a/src/structs/quantization_tables.rs +++ b/src/structs/quantization_tables.rs @@ -51,7 +51,7 @@ impl QuantizationTables { freq_max /= retval.quantization_table[coord]; } - let max_len = u16_bit_length(freq_max) as u8; + let max_len = u16_bit_length(freq_max); if max_len > RESIDUAL_NOISE_FLOOR as u8 { retval.min_noise_threshold[i] = max_len - RESIDUAL_NOISE_FLOOR as u8; } diff --git a/src/structs/row_spec.rs b/src/structs/row_spec.rs index d026285..367a6e2 100644 --- a/src/structs/row_spec.rs +++ b/src/structs/row_spec.rs @@ -8,12 +8,12 @@ use crate::consts::COLOR_CHANNEL_NUM_BLOCK_TYPES; use crate::structs::block_based_image::BlockBasedImage; pub struct RowSpec { - pub min_row_luma_y: i32, - pub next_row_luma_y: i32, - pub luma_y: i32, + pub min_row_luma_y: u32, + pub next_row_luma_y: u32, + pub luma_y: u32, pub component: usize, - pub curr_y: i32, - pub mcu_row_index: i32, + pub curr_y: u32, + pub mcu_row_index: u32, pub last_row_to_complete_mcu: bool, pub skip: bool, pub done: bool, @@ -23,7 +23,7 @@ impl RowSpec { pub fn get_row_spec_from_index( decode_index: u32, image_data: &[BlockBasedImage], - mcuv: i32, // number of mcus + mcuv: u32, // number of mcus max_coded_heights: &[u32], ) -> RowSpec { assert!( @@ -38,20 +38,20 @@ impl RowSpec { let mut mcu_multiple = 0; for i in 0..num_cmp { - heights.push(image_data[i].get_original_height() as u32); - component_multiple.push(heights[i] / mcuv as u32); + heights.push(image_data[i].get_original_height()); + component_multiple.push(heights[i] / mcuv); mcu_multiple += component_multiple[i]; } let mcu_row = decode_index / mcu_multiple; - let min_row_luma_y = (mcu_row * component_multiple[0]) as i32; + let min_row_luma_y = mcu_row * component_multiple[0]; let mut retval = RowSpec { skip: false, done: false, - mcu_row_index: mcu_row as i32, + mcu_row_index: mcu_row, component: num_cmp, min_row_luma_y, - next_row_luma_y: min_row_luma_y + component_multiple[0] as i32, + next_row_luma_y: min_row_luma_y + component_multiple[0], luma_y: min_row_luma_y, curr_y: 0, last_row_to_complete_mcu: false, @@ -63,11 +63,11 @@ impl RowSpec { loop { if place_within_scan < component_multiple[i] { retval.component = i; - retval.curr_y = ((mcu_row * component_multiple[i]) + place_within_scan) as i32; + retval.curr_y = (mcu_row * component_multiple[i]) + place_within_scan; retval.last_row_to_complete_mcu = (place_within_scan + 1 == component_multiple[i]) && (i == 0); - if retval.curr_y >= max_coded_heights[i] as i32 { + if retval.curr_y >= max_coded_heights[i] { retval.skip = true; retval.done = true; // assume true, but if we find something that needs coding, set false for j in 0..num_cmp - 1 { diff --git a/src/structs/simple_hash.rs b/src/structs/simple_hash.rs index 821cd17..f36f684 100644 --- a/src/structs/simple_hash.rs +++ b/src/structs/simple_hash.rs @@ -31,7 +31,7 @@ impl SimpleHashProvider for u32 { impl SimpleHashProvider for u64 { fn get_u64(&self) -> u64 { - return *self as u64; + return *self; } } @@ -41,7 +41,7 @@ impl SimpleHash { } pub fn hash(&mut self, v: T) { - self.hash = (Wrapping(self.hash as u64) * Wrapping(13u64) + Wrapping(v.get_u64())).0; + self.hash = (Wrapping(self.hash) * Wrapping(13u64) + Wrapping(v.get_u64())).0; } pub fn get(&self) -> u32 { diff --git a/src/structs/thread_handoff.rs b/src/structs/thread_handoff.rs index 67247f0..0d65d08 100644 --- a/src/structs/thread_handoff.rs +++ b/src/structs/thread_handoff.rs @@ -12,10 +12,10 @@ use crate::consts::COLOR_CHANNEL_NUM_BLOCK_TYPES; #[derive(Debug, Clone, PartialEq)] pub struct ThreadHandoff { - pub luma_y_start: i32, - pub luma_y_end: i32, - pub segment_offset_in_file: i32, - pub segment_size: i32, + pub luma_y_start: u32, + pub luma_y_end: u32, + pub segment_offset_in_file: u32, + pub segment_size: u32, pub overhang_byte: u8, pub num_overhang_bits: u8, pub last_dc: [i16; 4], @@ -27,10 +27,10 @@ impl ThreadHandoff { for _i in 0..num_threads { let mut th = ThreadHandoff { - luma_y_start: data.read_u16::()? as i32, + luma_y_start: data.read_u16::()? as u32, luma_y_end: 0, // filled in later segment_offset_in_file: 0, // not serialized - segment_size: data.read_i32::()?, + segment_size: data.read_u32::()?, overhang_byte: data.read_u8()?, num_overhang_bits: data.read_u8()?, last_dc: [0; 4], @@ -92,7 +92,7 @@ impl ThreadHandoff { overhang_byte: from.overhang_byte, num_overhang_bits: from.num_overhang_bits, luma_y_end: to.luma_y_end, - segment_size: ThreadHandoff::get_combine_thread_range_segment_size(from, to) as i32, + segment_size: ThreadHandoff::get_combine_thread_range_segment_size(from, to) as u32, last_dc: from.last_dc, }; diff --git a/src/structs/truncate_components.rs b/src/structs/truncate_components.rs index c872333..513aa39 100644 --- a/src/structs/truncate_components.rs +++ b/src/structs/truncate_components.rs @@ -11,9 +11,9 @@ use crate::structs::jpeg_header::JPegHeader; #[derive(Debug, Clone)] struct TrucateComponentsInfo { - trunc_bcv: i32, // the number of vertical components in this (truncated) image + trunc_bcv: u32, // the number of vertical components in this (truncated) image - trunc_bc: i32, + trunc_bc: u32, } #[derive(Debug, Clone)] @@ -22,9 +22,9 @@ pub struct TruncateComponents { pub components_count: usize, - pub mcu_count_horizontal: i32, + pub mcu_count_horizontal: u32, - pub mcu_count_vertical: i32, + pub mcu_count_vertical: u32, } impl Default for TruncateComponents { @@ -40,8 +40,8 @@ impl Default for TruncateComponents { impl TruncateComponents { pub fn init(&mut self, jpeg_header: &JPegHeader) { - self.mcu_count_horizontal = jpeg_header.mcuh; - self.mcu_count_vertical = jpeg_header.mcuv; + self.mcu_count_horizontal = jpeg_header.mcuh.get(); + self.mcu_count_vertical = jpeg_header.mcuv.get(); self.components_count = jpeg_header.cmpc; for i in 0..jpeg_header.cmpc { @@ -56,12 +56,12 @@ impl TruncateComponents { let mut retval = Vec::::new(); for i in 0..self.components_count { - retval.push(self.trunc_info[i].trunc_bcv as u32); + retval.push(self.trunc_info[i].trunc_bcv); } return retval; } - pub fn set_truncation_bounds(&mut self, jpeg_header: &JPegHeader, max_d_pos: [i32; 4]) { + pub fn set_truncation_bounds(&mut self, jpeg_header: &JPegHeader, max_d_pos: [u32; 4]) { for i in 0..self.components_count { TruncateComponents::set_block_count_d_pos( &mut self.trunc_info[i], @@ -72,15 +72,15 @@ impl TruncateComponents { } } - pub fn get_block_height(&self, cmp: usize) -> i32 { + pub fn get_block_height(&self, cmp: usize) -> u32 { return self.trunc_info[cmp].trunc_bcv; } fn set_block_count_d_pos( ti: &mut TrucateComponentsInfo, ci: &ComponentInfo, - trunc_bc: i32, - mcu_count_vertical: i32, + trunc_bc: u32, + mcu_count_vertical: u32, ) { assert!( ci.bcv == (ci.bc / ci.bch) + (if ci.bc % ci.bch != 0 { 1 } else { 0 }), @@ -105,12 +105,12 @@ impl TruncateComponents { ti.trunc_bc = trunc_bc; } - fn get_min_vertical_extcmp_multiple(cmp_info: &ComponentInfo, mcu_count_vertical: i32) -> i32 { + fn get_min_vertical_extcmp_multiple(cmp_info: &ComponentInfo, mcu_count_vertical: u32) -> u32 { let luma_height = cmp_info.bcv; return luma_height / mcu_count_vertical; } - pub fn get_component_sizes_in_blocks(&self) -> Vec { + pub fn get_component_sizes_in_blocks(&self) -> Vec { let mut retval = Vec::new(); for i in 0..self.components_count { retval.push(self.trunc_info[i].trunc_bc);