diff --git a/Cargo.lock b/Cargo.lock index 669f685..9c1ac9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,26 +271,6 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" -[[package]] -name = "lz4" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" -dependencies = [ - "libc", - "lz4-sys", -] - -[[package]] -name = "lz4-sys" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "memchr" version = "2.7.2" @@ -420,6 +400,16 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "rust-lzma" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d62915608f6cee1d7f2fc00f28b4f058ff79d6e4ec3c2fe0006b09b52437c84" +dependencies = [ + "pkg-config", + "vcpkg", +] + [[package]] name = "servicepoint-binding-cs" version = "0.1.0" @@ -435,7 +425,7 @@ dependencies = [ "bzip2", "flate2", "log", - "lz4", + "rust-lzma", "zstd", ] @@ -479,6 +469,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 7c4a4b8..c560d21 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -25,7 +25,7 @@ fn main() { connection .send(Command::BitmapLinearWin(Origin::top_left(), field.clone()).into()) .expect("could not send"); - thread::sleep(Duration::from_millis(14)); + thread::sleep(Duration::from_millis(30)); field = iteration(field); } } diff --git a/examples/wiping_clear/src/main.rs b/examples/wiping_clear/src/main.rs index ed8be0d..bec8385 100644 --- a/examples/wiping_clear/src/main.rs +++ b/examples/wiping_clear/src/main.rs @@ -3,9 +3,7 @@ use std::time::Duration; use clap::Parser; -use servicepoint2::{ - BitVec, Command, CompressionCode, Connection, PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid -}; +use servicepoint2::{BitVec, Command, CompressionCode, Connection, Origin, PIXEL_HEIGHT, PIXEL_WIDTH, PixelGrid}; #[derive(Parser, Debug)] struct Cli { @@ -20,6 +18,12 @@ fn main() { let cli = Cli::parse(); let connection = Connection::open(cli.destination).unwrap(); + + let mut buf = PixelGrid::max_sized(); + buf.fill(true); + connection.send(Command::BitmapLinearWin(Origin(0, 0), buf).into()) + .expect("send failed"); + let sleep_duration = Duration::from_millis(cli.time / PIXEL_WIDTH as u64); let mut enabled_pixels = @@ -36,7 +40,7 @@ fn main() { let bit_vec = BitVec::from(&*pixel_data); connection - .send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Gz).into()) + .send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma).into()) .unwrap(); thread::sleep(sleep_duration); } diff --git a/servicepoint2-binding-c/cbindgen.toml b/servicepoint2-binding-c/cbindgen.toml index dca125d..b10b660 100644 --- a/servicepoint2-binding-c/cbindgen.toml +++ b/servicepoint2-binding-c/cbindgen.toml @@ -19,11 +19,10 @@ sort_by = "Name" usize_is_size_t = true [defines] -"feature = compression-gz" = "SP2_FEATURE_compression_gz" -"feature = compression-bz" = "SP2_FEATURE_compression_bz" -"feature = compression-lz" = "SP2_FEATURE_compression_lz" -"feature = compression-zs" = "SP2_FEATURE_compression_zs" -#"feature = c-api" = "SP2_FEATURE_c-api" +"feature = compression_zlib" = "SP2_FEATURE_compression_zlib" +"feature = compression_bzip2" = "SP2_FEATURE_compression_bzip2" +"feature = compression_lzma" = "SP2_FEATURE_compression_lzma" +"feature = compression_zstd" = "SP2_FEATURE_compression_zstd" [export] prefix = "sp2_" diff --git a/servicepoint2-binding-c/servicepoint2.h b/servicepoint2-binding-c/servicepoint2.h index b8b0681..717bf35 100644 --- a/servicepoint2-binding-c/servicepoint2.h +++ b/servicepoint2-binding-c/servicepoint2.h @@ -45,17 +45,17 @@ enum sp2_CompressionCode #endif // __cplusplus { Uncompressed = 0, -#if defined(SP2_FEATURE_compression_gz) - Gz = 26490, +#if defined(SP2_FEATURE_compression_zlib) + Zlib = 26490, #endif -#if defined(SP2_FEATURE_compression_bz) - Bz = 25210, +#if defined(SP2_FEATURE_compression_bzip2) + Bzip2 = 25210, #endif -#if defined(SP2_FEATURE_compression_lz) - Lz = 27770, +#if defined(SP2_FEATURE_compression_lzma) + Lzma = 27770, #endif -#if defined(SP2_FEATURE_compression_zs) - Zs = 31347, +#if defined(SP2_FEATURE_compression_zstd) + Zstd = 31347, #endif }; #ifndef __cplusplus diff --git a/servicepoint2-binding-cs/src/BindGen/ServicePoint2.g.cs b/servicepoint2-binding-cs/src/BindGen/ServicePoint2.g.cs index c94c6c0..f4e3d53 100644 --- a/servicepoint2-binding-cs/src/BindGen/ServicePoint2.g.cs +++ b/servicepoint2-binding-cs/src/BindGen/ServicePoint2.g.cs @@ -276,10 +276,10 @@ namespace ServicePoint2.BindGen public enum CompressionCode : ushort { Uncompressed = 0, - Gz = 26490, - Bz = 25210, - Lz = 27770, - Zs = 31347, + Zlib = 26490, + Bzip2 = 25210, + Lzma = 27770, + Zstd = 31347, } diff --git a/servicepoint2/Cargo.toml b/servicepoint2/Cargo.toml index 83a05fb..13258af 100644 --- a/servicepoint2/Cargo.toml +++ b/servicepoint2/Cargo.toml @@ -16,14 +16,14 @@ crate-type = ["staticlib", "rlib", "cdylib"] log = "0.4" flate2 = { version = "1.0", optional = true } bzip2 = { version = "0.4", optional = true } -lz4 = { version = "1.24", optional = true } zstd = { version = "0.13", optional = true } +rust-lzma = { version = "0.6.0", optional = true } [features] -default = ["compression-gz", "compression-bz", "compression-lz", "compression-zs"] -compression-gz = ["dep:flate2", "compression"] -compression-bz = ["dep:bzip2", "compression"] -compression-lz = ["dep:lz4", "compression"] -compression-zs = ["dep:zstd", "compression"] +default = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"] +compression_zlib = ["dep:flate2", "compression"] +compression_bzip2 = ["dep:bzip2", "compression"] +compression_lzma = ["dep:rust-lzma", "compression"] +compression_zstd = ["dep:zstd", "compression"] compression = [] c-api = [] diff --git a/servicepoint2/src/command.rs b/servicepoint2/src/command.rs index 7708be3..4b7b2fe 100644 --- a/servicepoint2/src/command.rs +++ b/servicepoint2/src/command.rs @@ -252,12 +252,13 @@ fn bitmap_linear_into_packet( compression: CompressionCode, payload: Vec, ) -> Packet { + let length = payload.len() as u16; let payload = into_compressed(compression, payload); Packet( Header( command.into(), offset, - payload.len() as u16, + length, compression.into(), 0, ), @@ -290,12 +291,6 @@ fn packet_into_linear_bitmap( if reserved != 0 { return Err(TryFromPacketError::ExtraneousHeaderValues); } - if payload.len() != length as usize { - return Err(TryFromPacketError::UnexpectedPayloadSize( - length as usize, - payload.len(), - )); - } let sub = match CompressionCode::try_from(sub) { Err(_) => return Err(TryFromPacketError::InvalidCompressionCode(sub)), Ok(value) => value, @@ -304,6 +299,12 @@ fn packet_into_linear_bitmap( None => return Err(TryFromPacketError::DecompressionFailed), Some(value) => value, }; + if payload.len() != length as usize { + return Err(TryFromPacketError::UnexpectedPayloadSize( + length as usize, + payload.len(), + )); + } Ok((BitVec::from(&*payload), sub)) } diff --git a/servicepoint2/src/compression.rs b/servicepoint2/src/compression.rs index a8ec1a8..eec6e9a 100644 --- a/servicepoint2/src/compression.rs +++ b/servicepoint2/src/compression.rs @@ -1,33 +1,40 @@ -#[cfg(feature = "compression-bz")] -use bzip2::read::{BzDecoder, BzEncoder}; -#[cfg(feature = "compression-gz")] -use flate2::read::{GzDecoder, GzEncoder}; -#[cfg(feature = "compression-lz")] -use lz4::{Decoder as Lz4Decoder, EncoderBuilder as Lz4EncoderBuilder}; #[cfg(feature = "compression")] use std::io::{Read, Write}; -#[cfg(feature = "compression-zs")] + +#[cfg(feature = "compression_bzip2")] +use bzip2::read::{BzDecoder, BzEncoder}; +use flate2::{FlushCompress, FlushDecompress, Status}; +#[cfg(feature = "compression_lzma")] +use lzma; +#[cfg(feature = "compression_zstd")] use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder}; use crate::{CompressionCode, Payload}; pub(crate) fn into_decompressed( kind: CompressionCode, - payload: Payload, + mut payload: Payload, ) -> Option { match kind { CompressionCode::Uncompressed => Some(payload), - #[cfg(feature = "compression-gz")] - CompressionCode::Gz => { - let mut decoder = GzDecoder::new(&*payload); - let mut decompressed = vec![]; - match decoder.read_to_end(&mut decompressed) { - Err(_) => None, - Ok(_) => Some(decompressed), + #[cfg(feature = "compression_zlib")] + CompressionCode::Zlib => { + let mut decompress = flate2::Decompress::new(true); + let mut buffer = [0u8; 10000]; + + let status = match decompress.decompress(&*payload, &mut buffer, FlushDecompress::Finish) { + Err(_) => return None, + Ok(status) => status, + }; + + match status { + Status::Ok => None, + Status::BufError => None, + Status::StreamEnd => Some(buffer[0..(decompress.total_out() as usize)].to_owned()), } } - #[cfg(feature = "compression-bz")] - CompressionCode::Bz => { + #[cfg(feature = "compression_bzip2")] + CompressionCode::Bzip2 => { let mut decoder = BzDecoder::new(&*payload); let mut decompressed = vec![]; match decoder.read_to_end(&mut decompressed) { @@ -35,20 +42,12 @@ pub(crate) fn into_decompressed( Ok(_) => Some(decompressed), } } - #[cfg(feature = "compression-lz")] - CompressionCode::Lz => { - let mut decoder = match Lz4Decoder::new(&*payload) { - Err(_) => return None, - Ok(value) => value, - }; - let mut decompressed = vec![]; - match decoder.read_to_end(&mut decompressed) { - Err(_) => None, - Ok(_) => Some(decompressed), - } + #[cfg(feature = "compression_lzma")] + CompressionCode::Lzma => { + Some(lzma::decompress(&mut payload).unwrap()) } - #[cfg(feature = "compression-zs")] - CompressionCode::Zs => { + #[cfg(feature = "compression_zstd")] + CompressionCode::Zstd => { let mut decoder = match ZstdDecoder::new(&*payload) { Err(_) => return None, Ok(value) => value, @@ -68,18 +67,20 @@ pub(crate) fn into_compressed( ) -> Payload { match kind { CompressionCode::Uncompressed => payload, - #[cfg(feature = "compression-gz")] - CompressionCode::Gz => { - let mut encoder = - GzEncoder::new(&*payload, flate2::Compression::fast()); - let mut compressed = vec![]; - match encoder.read_to_end(&mut compressed) { - Err(err) => panic!("could not compress payload: {}", err), - Ok(_) => compressed, - } + #[cfg(feature = "compression_zlib")] + CompressionCode::Zlib => { + let mut compress = flate2::Compress::new(flate2::Compression::fast(), true); + let mut buffer = [0u8; 10000]; + + match compress.compress(&*payload, &mut buffer, FlushCompress::Finish).expect("compress failed") { + Status::Ok => panic!("buffer should be big enough"), + Status::BufError => panic!("BufError"), + Status::StreamEnd => {} + }; + buffer[..compress.total_out() as usize].to_owned() } - #[cfg(feature = "compression-bz")] - CompressionCode::Bz => { + #[cfg(feature = "compression_bzip2")] + CompressionCode::Bzip2 => { let mut encoder = BzEncoder::new(&*payload, bzip2::Compression::fast()); let mut compressed = vec![]; @@ -88,17 +89,12 @@ pub(crate) fn into_compressed( Ok(_) => compressed, } } - #[cfg(feature = "compression-lz")] - CompressionCode::Lz => { - let mut encoder = Lz4EncoderBuilder::new() - .build(vec![]) - .expect("could not create encoder"); - encoder.write_all(&payload).expect("could not write payload"); - let (payload, _) = encoder.finish(); - payload + #[cfg(feature = "compression_lzma")] + CompressionCode::Lzma => { + lzma::compress(&payload, 6).unwrap() } - #[cfg(feature = "compression-zs")] - CompressionCode::Zs => { + #[cfg(feature = "compression_zstd")] + CompressionCode::Zstd => { let mut encoder = ZstdEncoder::new(vec![], zstd::DEFAULT_COMPRESSION_LEVEL) .expect("could not create encoder"); diff --git a/servicepoint2/src/compression_code.rs b/servicepoint2/src/compression_code.rs index b3e96bd..631054f 100644 --- a/servicepoint2/src/compression_code.rs +++ b/servicepoint2/src/compression_code.rs @@ -5,14 +5,14 @@ use CompressionCode::*; #[derive(Debug, Clone, Copy)] pub enum CompressionCode { Uncompressed = 0x0, - #[cfg(feature = "compression-gz")] - Gz = 0x677a, - #[cfg(feature = "compression-bz")] - Bz = 0x627a, - #[cfg(feature = "compression-lz")] - Lz = 0x6c7a, - #[cfg(feature = "compression-zs")] - Zs = 0x7a73, + #[cfg(feature = "compression_zlib")] + Zlib = 0x677a, + #[cfg(feature = "compression_bzip2")] + Bzip2 = 0x627a, + #[cfg(feature = "compression_lzma")] + Lzma = 0x6c7a, + #[cfg(feature = "compression_zstd")] + Zstd = 0x7a73, } impl From for u16 { @@ -27,14 +27,14 @@ impl TryFrom for CompressionCode { fn try_from(value: u16) -> Result { match value { value if value == Uncompressed as u16 => Ok(Uncompressed), - #[cfg(feature = "compression-gz")] - value if value == Gz as u16 => Ok(Gz), - #[cfg(feature = "compression-bz")] - value if value == Bz as u16 => Ok(Bz), - #[cfg(feature = "compression-lz")] - value if value == Lz as u16 => Ok(Lz), - #[cfg(feature = "compression-zs")] - value if value == Zs as u16 => Ok(Zs), + #[cfg(feature = "compression_zlib")] + value if value == Zlib as u16 => Ok(Zlib), + #[cfg(feature = "compression_bzip2")] + value if value == Bzip2 as u16 => Ok(Bzip2), + #[cfg(feature = "compression_lzma")] + value if value == Lzma as u16 => Ok(Lzma), + #[cfg(feature = "compression_zstd")] + value if value == Zstd as u16 => Ok(Zstd), _ => Err(()), } }