Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix structs #52

Merged
merged 4 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions src/bulk.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::{error, Compress, DEVICE, QUALITY};
use crossbeam::channel;
use std::collections::VecDeque;
use std::fmt::Display;
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};
/// Custom configuration for building a [`Parallel`].
/// This struct is not meant to be used directly.
/// Use [`Parallel::from_vec`] instead.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct ParallelBuilder {
vec: VecDeque<(usize, Vec<u8>)>,
quality: u8,
Expand Down Expand Up @@ -62,8 +63,19 @@ impl ParallelBuilder {
}
}
}
impl Display for ParallelBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"len: {}\nquality: {}\ndevice_num: {}",
self.vec.len(),
self.quality,
self.device_num
)
}
}

#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct ToThread {
vec: VecDeque<(usize, Vec<u8>)>,
device_num: u8,
Expand Down Expand Up @@ -129,8 +141,19 @@ impl ToThread {
handles
}
}
impl Display for ToThread {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"len: {}\nquality: {}\ndevice_num: {}",
self.vec.len(),
self.quality,
self.device_num
)
}
}
/// Parallelized compression task.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Parallel {
to_thread: ToThread,
transmitter: channel::Sender<Result<Vec<u8>, error::Error>>,
Expand Down Expand Up @@ -178,6 +201,11 @@ impl Parallel {
handles
}
}
impl Display for Parallel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_thread)
}
}
impl IntoIterator for Parallel {
type Item = Result<Vec<u8>, error::Error>;
type IntoIter = ParallelIntoIterator;
Expand All @@ -189,6 +217,7 @@ impl IntoIterator for Parallel {
}

/// Target type when converting [`Parallel`] into an iterator.
#[derive(Debug, Clone)]
pub struct ParallelIntoIterator {
recv: channel::Receiver<Result<Vec<u8>, error::Error>>,
}
Expand Down
58 changes: 44 additions & 14 deletions src/single.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
use std::fmt::Display;

use crate::{error, Compress, QUALITY};
/// Custom configuration for building a [`Single`].
/// This struct is not meant to be used directly.
/// Use [`Single::from_bytes`] instead.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct SingleBuilder {
bytes_slice: Vec<u8>,
quality: u8,
}
impl SingleBuilder {
/// Specifies the quality of compressed images.
/// Defaults to 95 (95% original quality).
///
/// **This method is optional**.
pub fn with_quality(self, quality: u8) -> SingleBuilder {
SingleBuilder {
bytes_slice: self.bytes_slice,
quality,
}
}
}
impl SingleBuilder {
/// Builds a new Single with custom configurations.
/// # Example
Expand All @@ -36,9 +26,34 @@ impl SingleBuilder {
quality: self.quality,
}
}
/// Specifies the quality of compressed images.
/// Defaults to 95 (95% original quality).
///
/// **This method is optional**.
pub fn with_quality(self, quality: u8) -> SingleBuilder {
SingleBuilder {
bytes_slice: self.bytes_slice,
quality,
}
}
}
impl Display for SingleBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let to_show = self
.bytes_slice
.iter()
.take(8)
.map(|bytes| bytes)
.collect::<Vec<&u8>>();
write!(
f,
"bytes: {:#x?} (truncated)\nquality: {}",
to_show, self.quality
)
}
}
/// Single image compressions.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
pub struct Single {
bytes_slice: Vec<u8>,
quality: u8,
Expand Down Expand Up @@ -84,3 +99,18 @@ impl Single {
Ok(compress)
}
}
impl Display for Single {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let to_show = self
.bytes_slice
.iter()
.take(8)
.map(|bytes| bytes)
.collect::<Vec<&u8>>();
write!(
f,
"bytes: {:#x?} (truncated)\nquality: {}",
to_show, self.quality
)
}
}
88 changes: 84 additions & 4 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use jippigy::{Parallel, Single};
use std::io::Cursor;
use std::path::PathBuf;
use std::thread;

const TEST_DIR: &str = "./tests/images/";
struct Dummy {}
impl Dummy {
fn create_failing_image() -> Vec<u8> {
Expand Down Expand Up @@ -49,6 +49,86 @@ fn test_basic_success_parallel() {
}
}
#[test]
fn test_basic_single_eq() {
let test_dir_path = PathBuf::from(TEST_DIR);
let bytes = std::fs::read(test_dir_path.join("1.JPG")).unwrap();
let bytes2 = std::fs::read(test_dir_path.join("1.JPG")).unwrap();
let single1 = Single::from_bytes(bytes);
let single2 = Single::from_bytes(bytes2);
assert_eq!(single1, single2);
let built_single1 = single1.build();
let built_single2 = single2.build();
assert_eq!(built_single1, built_single2);
}
#[test]
fn test_basic_single_ineq() {
let test_dir_path = PathBuf::from(TEST_DIR);
let bytes = std::fs::read(test_dir_path.join("1.JPG")).unwrap();
let bytes2 = std::fs::read(test_dir_path.join("2.JPG")).unwrap();
let single1 = Single::from_bytes(bytes);
let single2 = Single::from_bytes(bytes2);
assert_ne!(single1, single2);
let built_single1 = single1.build();
let built_single2 = single2.build();
assert_ne!(built_single1, built_single2)
}
#[test]
fn test_basic_parallel_eq() {
let test_dir_path = PathBuf::from(TEST_DIR);
let vec = std::fs::read_dir(test_dir_path.clone())
.unwrap()
.flatten()
.filter(|direntry| direntry.path().is_file())
.map(|direntry| std::fs::read(direntry.path()).unwrap())
.collect::<Vec<Vec<u8>>>();
let vec2 = std::fs::read_dir(test_dir_path)
.unwrap()
.flatten()
.filter(|direntry| direntry.path().is_file())
.map(|direntry| std::fs::read(direntry.path()).unwrap())
.collect::<Vec<Vec<u8>>>();
let parallel1 = Parallel::from_vec(vec);
let parallel2 = Parallel::from_vec(vec2);
assert_eq!(parallel1, parallel2);
}
#[test]
// make sure prints doesn't break anything.
fn test_print() {
let image_dir_path = PathBuf::from(format!("{}", TEST_DIR));
let img = image_dir_path.join("1.JPG");
let bytes = std::fs::read(img).unwrap();
let single = Single::from_bytes(bytes);
println!("{single}");
let single_built = single.build();
println!("{single_built}");

let read = std::fs::read_dir(image_dir_path).unwrap();
let mut vec_of_bytes = Vec::new();
let mut list_of_names = Vec::new();
for image in read {
let uw = image.unwrap();
if uw.path().is_file() {
let _push_str = {
let filename = uw
.path()
.file_name()
.and_then(|osstr| osstr.to_str())
.and_then(|a| Some(a.to_string()))
.unwrap_or_default();
list_of_names.push(filename);
};
let read_file = std::fs::read(uw.path());
vec_of_bytes.push(read_file.unwrap());
}
}
let builder = Parallel::from_vec(vec_of_bytes)
.with_quality(50)
.with_device(4);
println!("{builder}");
let built = builder.build();
println!("{built}");
}
#[test]
/// This test attempts to check ONLY the **ordering** of the input original JPEG files and output compressed files. This check takes a while (adds around 3 mins of overall test time on low spec hardware) and it uses around 3-4 GBs of RAM.
fn test_ordering() {
let test_img_path = "./tests/images/";
Expand All @@ -73,7 +153,7 @@ fn test_ordering() {
})
.collect::<Vec<Vec<u8>>>();
println!("loaded {} images", original.len());
let original_rbg8 = original
let original_rgb8 = original
.clone()
.into_iter()
.map(|b| {
Expand All @@ -82,7 +162,7 @@ fn test_ordering() {
.into_rgb8()
})
.collect::<Vec<_>>();
println!("converted {} images to rbg8", original_rbg8.len());
println!("converted {} images to rgb8", original_rgb8.len());
let compressed = Parallel::from_vec(original)
.with_quality(50)
.with_device(4)
Expand All @@ -97,7 +177,7 @@ fn test_ordering() {
.collect::<Vec<_>>();
println!("compressed {} images", compressed.len());
let mut handles = Vec::new();
for (bytes, filename_outer) in original_rbg8.iter().zip(filenames.clone()) {
for (bytes, filename_outer) in original_rgb8.iter().zip(filenames.clone()) {
for (compressed_bytes, filename_inner) in compressed.iter().zip(filenames.clone()) {
// This check assumes that two vectors `original_rgb8` and `compressed` is ordered in the exact same way with the order their paths are returned in `filenames`.

Expand Down
Loading