mirror of
https://github.com/G2-Games/lbee-utils.git
synced 2025-04-19 23:32:55 -05:00
Experimenting with gxhash and other speedups
This commit is contained in:
parent
721e61f98c
commit
9c2f15769f
4 changed files with 40 additions and 21 deletions
|
@ -17,4 +17,3 @@ inherits = "release"
|
||||||
lto = true
|
lto = true
|
||||||
strip = true
|
strip = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = "abort"
|
|
||||||
|
|
|
@ -17,3 +17,4 @@ rgb = "0.8"
|
||||||
|
|
||||||
# Only active on feature "png"
|
# Only active on feature "png"
|
||||||
image = { version = "0.25", default-features = false, features = ["png"], optional = true }
|
image = { version = "0.25", default-features = false, features = ["png"], optional = true }
|
||||||
|
gxhash = "3.4.1"
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub struct CompressionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompressionInfo {
|
impl CompressionInfo {
|
||||||
|
/// Write the compression information into a byte stream
|
||||||
pub fn write_into<T: WriteBytesExt + Write>(
|
pub fn write_into<T: WriteBytesExt + Write>(
|
||||||
&self,
|
&self,
|
||||||
output: &mut T,
|
output: &mut T,
|
||||||
|
@ -52,10 +53,11 @@ impl CompressionInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get info about the compression chunks
|
/// Get info about the compression chunks.
|
||||||
///
|
///
|
||||||
/// These are defined by a length value, followed by the number of data chunks
|
/// These are defined by a value specifying the number of chunks, followed by
|
||||||
/// that length value says split into compressed and original size u32 values
|
/// a number of data chunks, the number of which are specified by the previous
|
||||||
|
/// number, and are split into compressed and original size u32 values.
|
||||||
pub fn get_chunk_info<T: Seek + ReadBytesExt + Read>(
|
pub fn get_chunk_info<T: Seek + ReadBytesExt + Read>(
|
||||||
bytes: &mut T,
|
bytes: &mut T,
|
||||||
) -> Result<CompressionInfo, CzError> {
|
) -> Result<CompressionInfo, CzError> {
|
||||||
|
@ -212,8 +214,8 @@ fn decompress_lzw2(input_data: &[u8], size: usize) -> Vec<u8> {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compress(data: &[u8], size: usize) -> (Vec<u8>, CompressionInfo) {
|
pub fn compress(data: &[u8], mut size: usize) -> (Vec<u8>, CompressionInfo) {
|
||||||
let mut size = size;
|
use gxhash::HashMapExt;
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
size = 0xFEFD
|
size = 0xFEFD
|
||||||
}
|
}
|
||||||
|
@ -230,15 +232,21 @@ pub fn compress(data: &[u8], size: usize) -> (Vec<u8>, CompressionInfo) {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut dictionary = gxhash::HashMap::new();
|
||||||
loop {
|
loop {
|
||||||
(count, part_data, last) = compress_lzw(&data[offset..], size, last);
|
let timer = std::time::Instant::now();
|
||||||
|
(count, part_data, last) = compress_lzw(&data[offset..], size, last, &mut dictionary);
|
||||||
|
println!("Compression took {}ms", timer.elapsed().as_micros() as f32 / 1000.0);
|
||||||
|
dictionary.clear();
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
|
// No more data to compress
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += count;
|
offset += count;
|
||||||
|
|
||||||
for d in &part_data {
|
for d in &part_data {
|
||||||
output_buf.write_all(&d.to_le_bytes()).unwrap();
|
output_buf.write_u16::<LittleEndian>(*d).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
output_info.chunks.push(ChunkInfo {
|
output_info.chunks.push(ChunkInfo {
|
||||||
|
@ -252,6 +260,7 @@ pub fn compress(data: &[u8], size: usize) -> (Vec<u8>, CompressionInfo) {
|
||||||
if output_info.chunk_count == 0 {
|
if output_info.chunk_count == 0 {
|
||||||
panic!("No chunks compressed!")
|
panic!("No chunks compressed!")
|
||||||
} else if output_info.chunk_count != 1 {
|
} else if output_info.chunk_count != 1 {
|
||||||
|
// TODO: Find out why this is necessary
|
||||||
output_info.chunks[0].size_raw -= 1;
|
output_info.chunks[0].size_raw -= 1;
|
||||||
output_info.chunks[output_info.chunk_count - 1].size_raw += 1;
|
output_info.chunks[output_info.chunk_count - 1].size_raw += 1;
|
||||||
}
|
}
|
||||||
|
@ -261,29 +270,35 @@ pub fn compress(data: &[u8], size: usize) -> (Vec<u8>, CompressionInfo) {
|
||||||
(output_buf, output_info)
|
(output_buf, output_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compress_lzw(data: &[u8], size: usize, last: Vec<u8>) -> (usize, Vec<u16>, Vec<u8>) {
|
fn compress_lzw(
|
||||||
|
data: &[u8],
|
||||||
|
size: usize,
|
||||||
|
last: Vec<u8>,
|
||||||
|
dict: &mut gxhash::HashMap<Vec<u8>, u16>
|
||||||
|
) -> (usize, Vec<u16>, Vec<u8>) {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut dictionary = HashMap::new();
|
|
||||||
for i in 0..=255 {
|
for i in 0..=255 {
|
||||||
dictionary.insert(vec![i], i as u16);
|
dict.insert(vec![i], i as u16);
|
||||||
}
|
}
|
||||||
let mut dictionary_count = (dictionary.len() + 1) as u16;
|
let mut dictionary_count = (dict.len() + 1) as u16;
|
||||||
|
|
||||||
let mut element = Vec::new();
|
let mut element = Vec::new();
|
||||||
if !last.is_empty() {
|
if !last.is_empty() {
|
||||||
element = last
|
element = last
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut compressed = Vec::with_capacity(size);
|
let mut entry: Vec<u8>;
|
||||||
|
let mut compressed = Vec::new();
|
||||||
for c in data {
|
for c in data {
|
||||||
let mut entry = element.clone();
|
entry = element.clone();
|
||||||
entry.push(*c);
|
entry.push(*c);
|
||||||
|
|
||||||
if dictionary.contains_key(&entry) {
|
if dict.contains_key(&entry) {
|
||||||
element = entry
|
element = entry
|
||||||
} else {
|
} else {
|
||||||
compressed.push(*dictionary.get(&element).unwrap());
|
compressed.push(*dict.get(&element).unwrap());
|
||||||
dictionary.insert(entry, dictionary_count);
|
dict.insert(entry, dictionary_count);
|
||||||
element = vec![*c];
|
element = vec![*c];
|
||||||
dictionary_count += 1;
|
dictionary_count += 1;
|
||||||
}
|
}
|
||||||
|
@ -299,13 +314,13 @@ fn compress_lzw(data: &[u8], size: usize, last: Vec<u8>) -> (usize, Vec<u16>, Ve
|
||||||
if compressed.is_empty() {
|
if compressed.is_empty() {
|
||||||
if !last_element.is_empty() {
|
if !last_element.is_empty() {
|
||||||
for c in last_element {
|
for c in last_element {
|
||||||
compressed.push(*dictionary.get(&vec![c]).unwrap());
|
compressed.push(*dict.get(&vec![c]).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (count, compressed, Vec::new());
|
return (count, compressed, Vec::new());
|
||||||
} else if compressed.len() < size {
|
} else if compressed.len() < size {
|
||||||
if !last_element.is_empty() {
|
if !last_element.is_empty() {
|
||||||
compressed.push(*dictionary.get(&last_element).unwrap());
|
compressed.push(*dict.get(&last_element).unwrap());
|
||||||
}
|
}
|
||||||
return (count, compressed, Vec::new());
|
return (count, compressed, Vec::new());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut cz_file = cz::open("test_file.cz3").unwrap();
|
let mut cz_file = cz::open("test_file.cz3").unwrap();
|
||||||
cz_file.save_as_png("test.png").unwrap();
|
cz_file.save_as_png("test.png").unwrap();
|
||||||
|
|
||||||
cz_file.header_mut().set_version(3).unwrap();
|
cz_file.header_mut().set_version(1).unwrap();
|
||||||
|
|
||||||
cz_file.save_as_cz("test_file.cz2").unwrap();
|
let timer = Instant::now();
|
||||||
|
cz_file.save_as_cz("unfixed.cz1").unwrap();
|
||||||
|
println!("Saving CZ took: {}ms", timer.elapsed().as_micros() as f32 / 1000.0);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue