about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2023-11-15 21:10:43 -0600
committerGitHub <noreply@github.com>2023-11-15 21:10:43 -0600
commit9fe0ced38710de9c9f4c36e182555ea80c8be20b (patch)
tree9bfa6080ce821166f045e0836a4b476b6c7fa827
parentfa2943a6a4bc1d276b458fefae48b28cd78cdb9c (diff)
parentad74e0bf2721ca9086cc3e2ecacfec91c54746f2 (diff)
downloadgifed-9fe0ced38710de9c9f4c36e182555ea80c8be20b.tar.gz
gifed-9fe0ced38710de9c9f4c36e182555ea80c8be20b.zip
Merge pull request #18 from novedevo/main
buncha clippy and devon-sourced changes
-rw-r--r--Cargo.toml1
-rw-r--r--gifed/Cargo.toml4
-rw-r--r--gifed/src/block/indexedimage.rs6
-rw-r--r--gifed/src/block/packed.rs2
-rw-r--r--gifed/src/block/palette.rs18
-rw-r--r--gifed/src/color.rs6
-rw-r--r--gifed/src/gif.rs28
-rw-r--r--gifed/src/lib.rs2
-rw-r--r--gifed/src/lzw.rs175
-rw-r--r--gifed/src/reader/mod.rs11
-rw-r--r--gifprobe/src/main.rs6
11 files changed, 160 insertions, 99 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 948388b..c3f707d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,2 +1,3 @@
 [workspace]
 members = ["gifed", "gifprobe", "gaudio", "gifcheck"]
+resolver = "2"
diff --git a/gifed/Cargo.toml b/gifed/Cargo.toml
index 79f86ce..a27ff7b 100644
--- a/gifed/Cargo.toml
+++ b/gifed/Cargo.toml
@@ -8,4 +8,8 @@ description = "Gif encoding and decoding with fine control"
 repository = "https://github.com/genuinebyte/gifed"
 
 [dependencies]
+bitvec = "1.0.1"
 weezl = "0.1.5"
+
+[dev-dependencies]
+rand = "0.8.5"
diff --git a/gifed/src/block/indexedimage.rs b/gifed/src/block/indexedimage.rs
index 1868382..03c2119 100644
--- a/gifed/src/block/indexedimage.rs
+++ b/gifed/src/block/indexedimage.rs
@@ -17,7 +17,7 @@ impl IndexedImage {
 	}
 
 	pub fn top(&self) -> u16 {
-		self.image_descriptor.left
+		self.image_descriptor.top
 	}
 
 	pub fn width(&self) -> u16 {
@@ -86,7 +86,7 @@ impl CompressedImage {
 	}
 
 	pub fn top(&self) -> u16 {
-		self.image_descriptor.left
+		self.image_descriptor.top
 	}
 
 	pub fn width(&self) -> u16 {
@@ -126,7 +126,7 @@ impl CompressedImage {
 			blocks,
 		} = self;
 
-		let data: Vec<u8> = blocks.into_iter().map(<_>::into_iter).flatten().collect();
+		let data: Vec<u8> = blocks.into_iter().flat_map(<_>::into_iter).collect();
 
 		//TODO: remove unwrap
 		let mut decompressor = weezl::decode::Decoder::new(weezl::BitOrder::Msb, lzw_code_size);
diff --git a/gifed/src/block/packed.rs b/gifed/src/block/packed.rs
index 455f43a..0752e39 100644
--- a/gifed/src/block/packed.rs
+++ b/gifed/src/block/packed.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::unusual_byte_groupings)]
+
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub struct GraphicPacked {
 	pub raw: u8,
diff --git a/gifed/src/block/palette.rs b/gifed/src/block/palette.rs
index d5cc696..9c4a5a9 100644
--- a/gifed/src/block/palette.rs
+++ b/gifed/src/block/palette.rs
@@ -35,10 +35,14 @@ impl Palette {
 		self.table.len()
 	}
 
+	pub fn is_empty(&self) -> bool {
+		self.len() > 0
+	}
+
 	/// Returns the number of items that the decoder *thinks* is in the palette.
 	/// This is 2^(n + 1) where n = [Palette::packed_len]
 	pub fn computed_len(&self) -> usize {
-		2usize.pow(self.packed_len() as u32 + 1)
+		1 << (self.packed_len() + 1)
 	}
 
 	/// Pushes a color on to the end of the table
@@ -47,7 +51,7 @@ impl Palette {
 	}
 
 	pub fn get(&self, index: u8) -> Option<Color> {
-		self.table.get(index as usize).map(|v| v.clone())
+		self.table.get(index as usize).copied()
 	}
 
 	pub fn from_color<C: AsRef<Color>>(&self, color: C) -> Option<u8> {
@@ -64,7 +68,7 @@ impl Palette {
 	//TODO: gen- better docs
 	pub fn padding(&self) -> usize {
 		let comp = self.computed_len();
-		(comp as usize - self.len()) * 3
+		(comp - self.len()) * 3
 	}
 
 	pub fn as_bytes(&self) -> Vec<u8> {
@@ -77,6 +81,12 @@ impl Palette {
 	}
 }
 
+impl Default for Palette {
+	fn default() -> Self {
+		Self::new()
+	}
+}
+
 impl Deref for Palette {
 	type Target = [Color];
 
@@ -97,7 +107,7 @@ impl TryFrom<&[u8]> for Palette {
 
 	fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
 		if value.len() % 3 != 0 {
-			return Err(());
+			Err(())
 		} else {
 			Ok(Self {
 				table: value
diff --git a/gifed/src/color.rs b/gifed/src/color.rs
index e1b727a..9f2bbe5 100644
--- a/gifed/src/color.rs
+++ b/gifed/src/color.rs
@@ -37,8 +37,8 @@ impl From<(u8, u8, u8)> for Color {
 	}
 }
 
-impl Into<[u8; 3]> for Color {
-	fn into(self) -> [u8; 3] {
-		[self.r, self.g, self.b]
+impl From<Color> for [u8; 3] {
+	fn from(val: Color) -> Self {
+		[val.r, val.g, val.b]
 	}
 }
diff --git a/gifed/src/gif.rs b/gifed/src/gif.rs
index 09b052e..90354a1 100644
--- a/gifed/src/gif.rs
+++ b/gifed/src/gif.rs
@@ -23,7 +23,7 @@ impl Gif {
 	pub fn as_bytes(&self) -> Vec<u8> {
 		let mut out = vec![];
 
-		out.extend_from_slice(&self.header.as_bytes());
+		out.extend_from_slice(self.header.as_bytes());
 		out.extend_from_slice(&self.screen_descriptor.as_bytes());
 
 		if let Some(gct) = &self.global_color_table {
@@ -44,7 +44,7 @@ impl Gif {
 		File::create(path.as_ref())?.write_all(&self.as_bytes())
 	}
 
-	pub fn images<'a>(&'a self) -> ImageIterator<'a> {
+	pub fn images(&self) -> ImageIterator<'_> {
 		ImageIterator {
 			gif: self,
 			block_index: 0,
@@ -64,24 +64,19 @@ impl<'a> Iterator for ImageIterator<'a> {
 		let starting_block = self.block_index;
 
 		let img = loop {
-			match self.gif.blocks.get(self.block_index) {
-				Some(block) => match block {
-					Block::CompressedImage(img) => {
-						// Step over this image so we don't hit it next time
-						self.block_index += 1;
-
-						break img;
-					}
-					_ => (),
-				},
-				None => return None,
+			let block = self.gif.blocks.get(self.block_index)?;
+			if let Block::CompressedImage(img) = block {
+				// Step over this image so we don't hit it next time
+				self.block_index += 1;
+
+				break img;
 			}
 
 			self.block_index += 1;
 		};
 
 		Some(Image {
-			compressed: &img,
+			compressed: img,
 			global_palette: self.gif.global_color_table.as_ref(),
 			blocks: &self.gif.blocks[starting_block..self.block_index],
 		})
@@ -123,8 +118,7 @@ impl<'a> Image<'a> {
 
 	pub fn transparent_index(&self) -> Option<u8> {
 		self.graphic_control()
-			.map(|gce| gce.transparent_index())
-			.flatten()
+			.and_then(|gce| gce.transparent_index())
 	}
 
 	pub fn frame_control(&self) -> Option<FrameControl> {
@@ -193,7 +187,7 @@ pub enum FrameControl {
 }
 
 #[cfg(test)]
-pub mod gif {
+pub mod gif_test {
 	use std::convert::TryInto;
 	use std::io::Write;
 
diff --git a/gifed/src/lib.rs b/gifed/src/lib.rs
index 966b46c..cfb124f 100644
--- a/gifed/src/lib.rs
+++ b/gifed/src/lib.rs
@@ -16,7 +16,7 @@ pub use lzw::LZW;
 /// Perform the algorithm to get the length of a color table from
 /// the value of the packed field. The max value here is 256
 pub(crate) fn packed_to_color_table_length(packed: u8) -> usize {
-	2usize.pow(packed as u32 + 1)
+	1 << (packed + 1)
 }
 
 //TODO: Be sure to check that fields in LSD and Img. Desc. that were reserved
diff --git a/gifed/src/lzw.rs b/gifed/src/lzw.rs
index 9fdfcdd..89b43e5 100644
--- a/gifed/src/lzw.rs
+++ b/gifed/src/lzw.rs
@@ -1,68 +1,99 @@
 use std::collections::HashMap;
 
-pub struct LZW {}
+use bitvec::prelude::*;
+
+pub struct LZW {
+	minimum_size: u8,
+	clear_code: u16,
+	end_of_information_code: u16,
+	dictionary: HashMap<Vec<u8>, u16>,
+}
 impl LZW {
-	pub fn encode(minimum_size: u8, indicies: &[u8]) -> Vec<u8> {
+	pub fn new(minimum_size: u8) -> Self {
 		let mut dictionary: HashMap<Vec<u8>, u16> = HashMap::new();
 
-		let cc = 2u16.pow(minimum_size as u32);
-		let eoi = cc + 1;
+		let clear_code = 1 << minimum_size;
+		let end_of_information_code = clear_code + 1;
 
-		println!("mcs {} | cc {}", minimum_size, cc);
+		println!("mcs {minimum_size} | cc {clear_code}");
 
 		// Fill dictionary with self-descriptive values
-		for value in 0..cc {
+		for value in 0..clear_code {
 			dictionary.insert(vec![value as u8], value);
 		}
 
-		let mut next_code = eoi + 1;
-		let mut code_size = minimum_size + 1;
+		Self {
+			minimum_size,
+			clear_code,
+			end_of_information_code,
+			dictionary,
+		}
+	}
+
+	pub fn reset(&mut self) {
+		*self = Self::new(self.minimum_size)
+	}
+
+	pub fn decode(&mut self, encoded: &[u8]) -> Vec<u8> {
+		let mut input = BitStream::new();
+		for &byte in encoded {
+			input.push_bits(8, byte as u16);
+		}
 
-		let mut iter = indicies.into_iter();
 		let mut out = BitStream::new();
-		let mut buffer = vec![*iter.next().unwrap()];
 
-		out.push_bits(code_size, cc);
+		todo!();
 
-		for &indicie in iter {
-			buffer.push(indicie);
+		out.vec()
+	}
+
+	pub fn encode(&mut self, indices: &[u8]) -> Vec<u8> {
+		let mut next_code = self.end_of_information_code + 1;
+		let mut code_size = self.minimum_size + 1;
 
-			if !dictionary.contains_key(&buffer) {
-				buffer.pop();
+		let mut iter = indices.iter();
+		let mut out = BitStream::new();
+		let mut buffer = vec![*iter.next().unwrap()];
 
-				if let Some(&code) = dictionary.get(&buffer) {
+		out.push_bits(code_size, self.clear_code);
+
+		for &index in iter {
+			buffer.push(index);
+
+			if !self.dictionary.contains_key(&buffer) {
+				if let Some(&code) = self.dictionary.get(&buffer[..buffer.len() - 1]) {
 					out.push_bits(code_size, code);
 
-					// Put the code back and add the vec to the dict
-					buffer.push(indicie);
-					dictionary.insert(buffer.clone(), next_code);
+					// add the vec to the dict
+					self.dictionary.insert(buffer, next_code);
 					next_code += 1;
 
 					// If the next_code can't fit in the code_size, we have to increase it
-					if next_code - 1 == 2u16.pow(code_size as u32) {
+					if next_code - 1 == 1 << code_size {
 						code_size += 1;
 					}
 
-					buffer.clear();
-					buffer.push(indicie);
+					buffer = vec![index];
 				} else {
-					println!("indicie is: {}", indicie);
+					println!("index is: {index}");
 					println!("buffer is: {:?}", buffer);
-					println!("dictionary: {:?}", dictionary);
+					println!("dictionary: {:?}", self.dictionary);
 					unreachable!()
 				}
 			}
 		}
 
-		if buffer.len() > 0 {
-			match dictionary.get(&buffer) {
+		if !buffer.is_empty() {
+			match self.dictionary.get(&buffer) {
 				Some(&code) => out.push_bits(code_size, code),
 				None => {
-					panic!("Codes left in the buffer but the buffer is not a valid dictionary key!")
+					unreachable!(
+						"Codes left in the buffer but the buffer is not a valid dictionary key!"
+					)
 				}
 			}
 		}
-		out.push_bits(code_size, eoi);
+		out.push_bits(code_size, self.end_of_information_code);
 
 		out.vec()
 	}
@@ -72,70 +103,92 @@ impl LZW {
 mod lzw_test {
 	use super::*;
 
+	fn rand_against_weezl(length: usize) {
+		let range = rand::distributions::Uniform::from(0..=1);
+		let indices = rand::Rng::sample_iter(rand::thread_rng(), &range)
+			.take(length)
+			.collect::<Vec<_>>();
+		let weezl = weezl::encode::Encoder::new(weezl::BitOrder::Lsb, 2)
+			.encode(&indices)
+			.unwrap();
+		let us = LZW::new(2).encode(&indices);
+
+		assert_eq!(us.len(), weezl.len());
+	}
+
+	#[test]
+	#[ignore]
+	fn fortyk_against_weezl() {
+		rand_against_weezl(40_000);
+	}
+
+	#[test]
+	#[ignore]
+	fn thirtyeightk_against_weezl() {
+		rand_against_weezl(38_000);
+	}
+
+	#[test]
+	#[ignore]
+	fn twentyk_against_weezl_repeated() {
+		for _ in 0..100 {
+			rand_against_weezl(20_000)
+		}
+	}
+
 	#[test]
 	fn encode() {
-		let indicies = vec![0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
+		let indices = vec![0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
 		let output = vec![0x84, 0x1D, 0x81, 0x7A, 0x50];
 
-		let lzout = LZW::encode(2, &indicies);
+		let lzout = LZW::new(2).encode(&indices);
 
 		assert_eq!(lzout, output);
 	}
 
 	#[test]
 	fn against_weezl() {
-		let indicies = vec![0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
+		let indices = vec![0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
 		let weezl = weezl::encode::Encoder::new(weezl::BitOrder::Lsb, 2)
-			.encode(&indicies)
+			.encode(&indices)
 			.unwrap();
-		let us = LZW::encode(2, &indicies);
+		let us = LZW::new(2).encode(&indices);
 
 		assert_eq!(weezl, us);
 	}
 }
 
 struct BitStream {
-	formed: Vec<u8>,
-	current: u8,
-	index: u8,
+	formed: BitVec<u8, Lsb0>,
 }
 
 impl BitStream {
 	fn new() -> Self {
 		Self {
-			formed: vec![],
-			current: 0,
-			index: 0,
+			formed: BitVec::EMPTY,
 		}
 	}
 
 	fn push_bits(&mut self, count: u8, data: u16) {
-		let mut new_index = self.index + count;
-		let mut current32 = (self.current as u32) | ((data as u32) << self.index);
-
-		loop {
-			if new_index >= 8 {
-				self.formed.push(current32 as u8);
-				current32 = current32 >> 8;
-				new_index -= 8;
-			} else {
-				self.current = current32 as u8;
-				self.index = new_index;
-
-				break;
-			}
+		for i in 0..count {
+			self.formed.push((data & (1 << i)) > 0)
 		}
 	}
 
-	fn vec(self) -> Vec<u8> {
-		let mut out = self.formed;
-
-		if self.index != 0 {
-			out.push(self.current);
+	fn pop_bits(&mut self, count: u8) -> u16 {
+		let mut out = 0;
+		for i in (0..count).filter_map(|_| self.formed.pop()) {
+			out <<= 1;
+			let int: u16 = i.into();
+			out |= int;
 		}
-
 		out
 	}
+
+	fn vec(mut self) -> Vec<u8> {
+		self.formed.set_uninitialized(false);
+		self.formed.into_vec()
+	}
 }
 
 #[cfg(test)]
@@ -155,7 +208,7 @@ mod bitstream_test {
 		for byte in &bsvec {
 			print!("{:b} ", byte);
 		}
-		println!("");
+		println!();
 
 		assert_eq!(bsvec, vec![0b1001_1111, 0b0000_0001]);
 	}
@@ -171,7 +224,7 @@ mod bitstream_test {
 		for byte in &bsvec {
 			print!("{:b} ", byte);
 		}
-		println!("");
+		println!();
 
 		assert_eq!(bsvec, vec![0b0000_0011, 0b0001_0000]);
 	}
diff --git a/gifed/src/reader/mod.rs b/gifed/src/reader/mod.rs
index aed2142..763f34e 100644
--- a/gifed/src/reader/mod.rs
+++ b/gifed/src/reader/mod.rs
@@ -22,7 +22,7 @@ pub struct Decoder<R: Read> {
 
 impl Decoder<BufReader<File>> {
 	pub fn file<P: AsRef<Path>>(path: P) -> Result<Self, DecodeError> {
-		let file = File::open(path).map_err(|e| DecodeError::IoError(e))?;
+		let file = File::open(path).map_err(DecodeError::IoError)?;
 		let buffreader = BufReader::new(file);
 		Ok(Decoder::new(buffreader))
 	}
@@ -61,11 +61,8 @@ impl<R: Read> Decoder<R> {
 		let mut decoder = self.read()?;
 
 		let mut blocks = vec![];
-		loop {
-			match decoder.block()? {
-				Some(block) => blocks.push(block.block),
-				None => break,
-			}
+		while let Some(block) = decoder.block()? {
+			blocks.push(block.block)
 		}
 
 		Ok(Gif {
@@ -283,7 +280,7 @@ impl<R: Read> SmartReader<R> {
 	}
 
 	pub fn read_palette(&mut self, count: usize) -> Result<Palette, DecodeError> {
-		let mut buf = vec![0; count as usize * 3];
+		let mut buf = vec![0; count * 3];
 		self.read_exact(&mut buf)?;
 
 		Ok(Palette::try_from(buf.as_slice()).unwrap())
diff --git a/gifprobe/src/main.rs b/gifprobe/src/main.rs
index 64cee94..fd6b189 100644
--- a/gifprobe/src/main.rs
+++ b/gifprobe/src/main.rs
@@ -10,7 +10,7 @@ use gifed::{
 use owo_colors::OwoColorize;
 
 fn main() {
-	let file = if let Some(file) = std::env::args().skip(1).next() {
+	let file = if let Some(file) = std::env::args().nth(1) {
 		file
 	} else {
 		println!("usage: gifprobe file.gif");
@@ -49,7 +49,7 @@ fn main() {
 		);
 
 		if colors {
-			for (idx, clr) in plt.into_iter().enumerate() {
+			for (idx, clr) in plt.iter().enumerate() {
 				println!(
 					"\t{} {}, {}, {}",
 					idx.color(owo_colors::Rgb(clr.r, clr.g, clr.b)),
@@ -217,7 +217,7 @@ fn describe_image(bli: CompressedImage, offset: Range<usize>, expand: bool, colo
 		);
 
 		if colors {
-			for (idx, clr) in plt.into_iter().enumerate() {
+			for (idx, clr) in plt.iter().enumerate() {
 				println!("\t{idx} {}, {}, {}", clr.r, clr.g, clr.b);
 			}
 		}