diff --git a/Cargo.toml b/Cargo.toml index 0a0106d..29de7c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ readme = "README.md" repository = "https://github.com/niklasf/rust-huffman-compress" categories = ["compression", "encoding", "algorithms"] description = "Huffman compression given a probability distribution over arbitrary symbols" +edition = "2021" [[bench]] name = "benches" @@ -18,7 +19,7 @@ bit-vec = "0.6" num-traits = "0.2" [dev-dependencies] -bencher = "0.1" +criterion = "0.5" quickcheck = "1" [badges] diff --git a/README.md b/README.md index e9bd7ed..651e967 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,9 @@ Documentation Changelog --------- +* to be released + - Switch to 2021 edition. + * 0.6.1 - Fix deprecation warning and remove `#[deny(warnings)]` (a future compatibility hazard in libraries). diff --git a/benches/benches.rs b/benches/benches.rs index e946df1..56de3d4 100644 --- a/benches/benches.rs +++ b/benches/benches.rs @@ -1,14 +1,9 @@ -#[macro_use] -extern crate bencher; -extern crate bit_vec; -extern crate huffman_compress; - -use bencher::{black_box, Bencher}; use bit_vec::BitVec; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; use huffman_compress::codebook; use std::collections::HashMap; -fn bench_encode_decode(b: &mut Bencher) { +fn bench_encode_decode(c: &mut Criterion) { let mut weights = HashMap::new(); weights.insert("CG", 293); weights.insert("AG", 34); @@ -20,19 +15,21 @@ fn bench_encode_decode(b: &mut Bencher) { let example = black_box(vec!["AT", "CG", "AT", "TG", "AG", "CT", "CT", "AG", "CG"]); - b.iter(|| { - let mut buffer = BitVec::new(); - for symbol in &example { - book.encode(&mut buffer, symbol).unwrap(); - } - - assert!(example - .iter() - .zip(tree.unbounded_decoder(&buffer)) - .all(|(l, r)| l == &r)); + c.bench_function("encode-decode", |b| { + b.iter(|| { + let mut buffer = BitVec::new(); + for symbol in &example { + book.encode(&mut buffer, symbol).unwrap(); + } + + assert!(example + .iter() + .zip(tree.unbounded_decoder(&buffer)) + .all(|(l, r)| l == &r)); + }) }); } -benchmark_group!(benches, bench_encode_decode); +criterion_group!(benches, bench_encode_decode); -benchmark_main!(benches); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs index 0ba7d6f..4e85b10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ //! //! # use std::error::Error; //! # -//! # fn try_main() -> Result<(), Box> { +//! # fn try_main() -> Result<(), Box> { //! use std::iter::FromIterator; //! use std::collections::HashMap; //! use bit_vec::BitVec; @@ -60,13 +60,8 @@ #![forbid(unsafe_code)] #![deny(missing_docs)] #![deny(missing_debug_implementations)] - -extern crate bit_vec; -extern crate num_traits; - -#[cfg(test)] -#[macro_use] -extern crate quickcheck; +#![warn(clippy::pedantic)] +#![allow(clippy::manual_let_else)] use std::borrow::Borrow; use std::cmp; @@ -80,7 +75,7 @@ use bit_vec::BitVec; use num_traits::ops::saturating::Saturating; -/// A trie used for decoding. +/// A tree used for decoding. #[derive(Debug, Clone)] pub struct Tree { root: usize, @@ -153,19 +148,16 @@ impl<'a, K: Clone, I: IntoIterator> Iterator for UnboundedDecoder<' type Item = K; fn next(&mut self) -> Option { - let mut node = match self.tree.arena.get(self.tree.root) { - Some(root) => root, - None => return None, // empty tree - }; + let mut node = self.tree.arena.get(self.tree.root)?; loop { match node.data { NodeData::Leaf { ref symbol } => return Some(symbol.clone()), NodeData::Branch { left, right } => { - node = match self.iter.next() { - Some(true) => &self.tree.arena[left], - Some(false) => &self.tree.arena[right], - None => return None, + node = if self.iter.next()? { + &self.tree.arena[left] + } else { + &self.tree.arena[right] }; } } @@ -181,6 +173,7 @@ pub struct Book { impl Book { /// Returns the underlying B-Tree. + #[must_use] pub fn into_inner(self) -> BTreeMap { self.book } @@ -196,11 +189,13 @@ impl Book { } /// Returns the number of symbols in the book. + #[must_use] pub fn len(&self) -> usize { self.book.len() } /// Returns true if the map has no symbols. + #[must_use] pub fn is_empty(&self) -> bool { self.book.is_empty() } @@ -235,12 +230,7 @@ impl Book { K: Borrow, Q: Ord, { - match self.book.get(k) { - Some(code) => buffer.extend(code), - None => return Err(EncodeError {}), - } - - Ok(()) + self.book.get(k).map(|code| buffer.extend(code)).ok_or(EncodeError {}) } fn new() -> Book { @@ -303,6 +293,7 @@ pub struct CodeBuilder { impl CodeBuilder { /// Creates a new, empty `CodeBuilder`. + #[must_use] pub fn new() -> CodeBuilder { CodeBuilder { heap: BinaryHeap::new(), @@ -312,6 +303,7 @@ impl CodeBuilder { /// Creates a new, empty `CodeBuilder` and preallocates space /// for `capacity` symbols. + #[must_use] pub fn with_capacity(capacity: usize) -> CodeBuilder { CodeBuilder { heap: BinaryHeap::with_capacity(capacity), @@ -335,6 +327,7 @@ impl CodeBuilder { /// Constructs a [book](struct.Book.html) and [tree](struct.Tree.html) pair /// for encoding and decoding. + #[must_use] pub fn finish(mut self) -> (Book, Tree) { let mut book = Book::new(); @@ -425,7 +418,7 @@ impl<'a, K: Ord + Clone, W: Saturating + Ord + Clone> FromIterator<(&'a K, &'a W where T: IntoIterator, { - CodeBuilder::from_iter(weights.into_iter().map(|(k, v)| (k.clone(), v.clone()))) + weights.into_iter().map(|(k, v)| (k.clone(), v.clone())).collect() } } @@ -469,6 +462,7 @@ where #[cfg(test)] mod tests { use super::*; + use quickcheck::quickcheck; use std::collections::HashMap; #[test] @@ -500,7 +494,7 @@ mod tests { #[test] fn test_uniform_from_static() { const WEIGHTS: &[(&char, &usize)] = &[(&'a', &1), (&'b', &1), (&'c', &1), (&'d', &1)]; - let (book, tree) = codebook(WEIGHTS.iter().cloned()); + let (book, tree) = codebook(WEIGHTS.iter().copied()); let mut buffer = BitVec::new(); book.encode(&mut buffer, &'a').unwrap(); @@ -552,11 +546,11 @@ mod tests { let (book, _) = builder.finish(); let len = |symbol| { - book.get(symbol).map_or(0, |code| code.len()) + book.get(symbol).map_or(0, bit_vec::BitVec::len) }; at >= ct || len("CT") <= len("AT") || - ag.saturating_add(at).saturating_add(cg).saturating_add(ct).saturating_add(tg) >= u32::MAX + ag.saturating_add(at).saturating_add(cg).saturating_add(ct).saturating_add(tg) == u32::MAX } fn encode_decode_bytes(symbols: Vec) -> bool {