diff --git a/Cargo.lock b/Cargo.lock index 7b3c177..3124d5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -159,6 +159,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "lockfree-object-pool" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69c0481fc2424cb55795de7da41add33372ea75a94f9b6588ab6a2826dfebc" + [[package]] name = "log" version = "0.4.21" @@ -184,6 +190,12 @@ dependencies = [ "libm", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -525,8 +537,10 @@ name = "zopfli" version = "0.8.0" dependencies = [ "crc32fast", + "lockfree-object-pool", "log", "miniz_oxide", + "once_cell", "proptest", "proptest-derive", "simd-adler32", diff --git a/Cargo.toml b/Cargo.toml index b51db18..1faf3f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ crc32fast = { version = "1.4.0", default-features = false, optional = true } simd-adler32 = { version = "0.3.7", default-features = false, optional = true } typed-arena = { version = "2.0.2", default-features = false } log = "0.4.21" +lockfree-object-pool = "0.1.5" +once_cell = "1.19.0" [dev-dependencies] proptest = "1.4.0" diff --git a/src/hash.rs b/src/hash.rs index 038adb0..6cc13f3 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -4,6 +4,9 @@ use alloc::{ }; use core::ptr::{addr_of, addr_of_mut, NonNull}; +use lockfree_object_pool::LinearObjectPool; +use once_cell::sync::Lazy; + use crate::util::{ZOPFLI_MIN_MATCH, ZOPFLI_WINDOW_MASK, ZOPFLI_WINDOW_SIZE}; const HASH_SHIFT: i32 = 5; @@ -176,3 +179,6 @@ impl ZopfliHash { } } } + +pub static HASH_POOL: Lazy>> = + Lazy::new(|| LinearObjectPool::new(ZopfliHash::new, |boxed| boxed.reset())); diff --git a/src/lib.rs b/src/lib.rs index 075af77..6b73e07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,8 @@ use std::io::{Error, Write}; #[cfg(any(doc, not(feature = "std")))] pub use io::{Error, ErrorKind, Write}; +use crate::hash::HASH_POOL; + /// Options for the Zopfli compression algorithm. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(all(test, feature = "std"), derive(proptest_derive::Arbitrary))] @@ -169,6 +171,12 @@ pub fn compress( } } +/// Populates object pools for expensive objects that Zopfli uses. Call this on a background thread +/// when you know ahead of time that compression will be needed. +pub fn prewarm_object_pools() { + HASH_POOL.pull(); +} + #[cfg(all(test, feature = "std"))] mod test { use std::io; diff --git a/src/lz77.rs b/src/lz77.rs index ac4c2a9..470c913 100644 --- a/src/lz77.rs +++ b/src/lz77.rs @@ -3,7 +3,7 @@ use core::cmp; use crate::{ cache::Cache, - hash::{Which, ZopfliHash}, + hash::{Which, ZopfliHash, HASH_POOL}, symbols::{get_dist_symbol, get_length_symbol}, util::{ boxed_array, ZOPFLI_MAX_CHAIN_HITS, ZOPFLI_MAX_MATCH, ZOPFLI_MIN_MATCH, ZOPFLI_NUM_D, @@ -139,7 +139,8 @@ impl Lz77Store { return; } let windowstart = instart.saturating_sub(ZOPFLI_WINDOW_SIZE); - let mut h = ZopfliHash::new(); + let hash_pool = &*HASH_POOL; + let mut h = hash_pool.pull(); let arr = &in_data[..inend]; h.warmup(arr, windowstart, inend); @@ -239,7 +240,8 @@ impl Lz77Store { return; } - let mut h = ZopfliHash::new(); + let hash_pool = &*HASH_POOL; + let mut h = hash_pool.pull(); let arr = &in_data[..inend]; h.warmup(arr, windowstart, inend); diff --git a/src/squeeze.rs b/src/squeeze.rs index 9b27746..6b79260 100644 --- a/src/squeeze.rs +++ b/src/squeeze.rs @@ -15,7 +15,7 @@ use log::{debug, trace}; use crate::{ cache::Cache, deflate::{calculate_block_size, BlockType}, - hash::ZopfliHash, + hash::{ZopfliHash, HASH_POOL}, lz77::{find_longest_match, LitLen, Lz77Store}, symbols::{get_dist_extra_bits, get_dist_symbol, get_length_extra_bits, get_length_symbol}, util::{ZOPFLI_MAX_MATCH, ZOPFLI_NUM_D, ZOPFLI_NUM_LL, ZOPFLI_WINDOW_MASK, ZOPFLI_WINDOW_SIZE}, @@ -421,7 +421,8 @@ pub fn lz77_optimal_fixed( inend: usize, store: &mut Lz77Store, ) { - let mut h = ZopfliHash::new(); + let hash_pool = &*HASH_POOL; + let mut h = hash_pool.pull(); let mut costs = Vec::with_capacity(inend - instart); lz77_optimal_run( lmc, @@ -455,7 +456,8 @@ pub fn lz77_optimal( let mut stats = SymbolStats::default(); stats.get_statistics(¤tstore); - let mut h = ZopfliHash::new(); + let hash_pool = &*HASH_POOL; + let mut h = hash_pool.pull(); let mut costs = Vec::with_capacity(inend - instart + 1); let mut beststats = SymbolStats::default();