Skip to content

Commit 3d564b8

Browse files
Folkert de Vriesfolkertdev
authored andcommitted
stable api v0
1 parent 47bf9cd commit 3d564b8

File tree

6 files changed

+528
-5
lines changed

6 files changed

+528
-5
lines changed

test-libz-rs-sys/src/deflate.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2703,3 +2703,94 @@ fn test_gzip_deflate_config() {
27032703
Some(dest)
27042704
});
27052705
}
2706+
2707+
mod stable_api {
2708+
use zlib_rs::{
2709+
inflate::{uncompress_slice, InflateConfig},
2710+
DeflateFlush, ReturnCode, Status,
2711+
};
2712+
2713+
#[test]
2714+
#[should_panic = "StreamError"]
2715+
fn invalid_config() {
2716+
zlib_rs::Deflate::new(9000, true, 15);
2717+
}
2718+
2719+
fn with_window_bits(window_bits: i32) {
2720+
let input = "Hello World!";
2721+
2722+
let mut deflate =
2723+
zlib_rs::Deflate::new(9, window_bits >= 0, window_bits.unsigned_abs() as u8);
2724+
let mut compressed = [0u8; 64];
2725+
let ret = deflate.compress(input.as_bytes(), &mut compressed, DeflateFlush::Finish);
2726+
assert_eq!(ret, Ok(Status::StreamEnd));
2727+
2728+
assert_eq!(deflate.total_in() as usize, input.len());
2729+
2730+
let compressed = &compressed[..deflate.total_out() as usize];
2731+
let mut decompressed = [0u8; 64];
2732+
let (decompressed, ret) =
2733+
uncompress_slice(&mut decompressed, compressed, InflateConfig { window_bits });
2734+
assert_eq!(ret, ReturnCode::Ok);
2735+
2736+
assert_eq!(decompressed, input.as_bytes());
2737+
}
2738+
2739+
#[test]
2740+
fn raw() {
2741+
with_window_bits(-15);
2742+
}
2743+
2744+
#[test]
2745+
fn zlib_header() {
2746+
with_window_bits(15);
2747+
}
2748+
2749+
#[test]
2750+
fn gz_header() {
2751+
with_window_bits(16 + 15);
2752+
}
2753+
2754+
#[test]
2755+
fn reset_reuse_stream() {
2756+
let input1 = "Hello World!";
2757+
let input2 = "Goodbye World!";
2758+
2759+
let mut deflate = zlib_rs::Deflate::new(9, true, 15);
2760+
2761+
let mut compressed1 = [0u8; 64];
2762+
let ret = deflate.compress(input1.as_bytes(), &mut compressed1, DeflateFlush::Finish);
2763+
assert_eq!(ret, Ok(Status::StreamEnd));
2764+
assert_eq!(deflate.total_in() as usize, input1.len());
2765+
2766+
let compressed1 = &compressed1[..deflate.total_out() as usize];
2767+
2768+
deflate.reset();
2769+
2770+
let mut compressed2 = [0u8; 64];
2771+
let ret = deflate.compress(input2.as_bytes(), &mut compressed2, DeflateFlush::Finish);
2772+
assert_eq!(ret, Ok(Status::StreamEnd));
2773+
// After reset, total_in should reflect only the second input
2774+
assert_eq!(deflate.total_in() as usize, input2.len());
2775+
2776+
let compressed2 = &compressed2[..deflate.total_out() as usize];
2777+
2778+
let mut decompressed1 = [0u8; 64];
2779+
let (decompressed1, ret) = uncompress_slice(
2780+
&mut decompressed1,
2781+
compressed1,
2782+
InflateConfig { window_bits: 15 },
2783+
);
2784+
assert_eq!(ret, ReturnCode::Ok);
2785+
assert_eq!(decompressed1, input1.as_bytes());
2786+
2787+
let mut decompressed2 = [0u8; 64];
2788+
let (decompressed2, ret) = uncompress_slice(
2789+
&mut decompressed2,
2790+
compressed2,
2791+
InflateConfig { window_bits: 15 },
2792+
);
2793+
assert_eq!(ret, ReturnCode::Ok);
2794+
assert_eq!(decompressed2, input2.as_bytes());
2795+
}
2796+
}

test-libz-rs-sys/src/inflate.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,3 +2404,148 @@ fn test_dict_inflate() {
24042404
assert_eq!(uncompr[..hello.len()], hello);
24052405
});
24062406
}
2407+
2408+
mod stable_api {
2409+
use zlib_rs::{
2410+
deflate::{compress_slice, DeflateConfig},
2411+
InflateError, ReturnCode,
2412+
};
2413+
2414+
fn with_window_bits(n: i32) {
2415+
let config = DeflateConfig {
2416+
window_bits: n,
2417+
..DeflateConfig::default()
2418+
};
2419+
2420+
let mut output = [0u8; 64];
2421+
let input = "Hello World!";
2422+
let (compressed, ret) = compress_slice(&mut output, input.as_bytes(), config);
2423+
assert_eq!(ret, ReturnCode::Ok);
2424+
2425+
let mut decompressed = [0u8; 64];
2426+
let mut inflate = zlib_rs::Inflate::new(n >= 0, n.unsigned_abs() as u8);
2427+
inflate
2428+
.decompress(compressed, &mut decompressed, zlib_rs::InflateFlush::Finish)
2429+
.unwrap();
2430+
2431+
assert_eq!(inflate.total_in() as usize, compressed.len());
2432+
2433+
assert_eq!(
2434+
&decompressed[..inflate.total_out() as usize],
2435+
input.as_bytes()
2436+
);
2437+
}
2438+
2439+
#[test]
2440+
fn raw() {
2441+
with_window_bits(-15);
2442+
}
2443+
2444+
#[test]
2445+
fn zlib_header() {
2446+
with_window_bits(15);
2447+
}
2448+
2449+
#[test]
2450+
fn gz_header() {
2451+
with_window_bits(16 + 15);
2452+
}
2453+
2454+
#[test]
2455+
#[should_panic = "StreamError"]
2456+
fn invalid_config() {
2457+
zlib_rs::Inflate::new(true, 123);
2458+
}
2459+
2460+
#[test]
2461+
fn invalid_data() {
2462+
let mut inflate = zlib_rs::Inflate::new(true, 15);
2463+
2464+
// Clearly invalid input.
2465+
let compressed = [0xAA; 64];
2466+
let mut decompressed = [0u8; 64];
2467+
2468+
let ret = inflate.decompress(
2469+
&compressed,
2470+
&mut decompressed,
2471+
zlib_rs::InflateFlush::Finish,
2472+
);
2473+
2474+
assert_eq!(ret, Err(InflateError::DataError));
2475+
}
2476+
2477+
#[test]
2478+
fn need_dict() {
2479+
let mut inflate = zlib_rs::Inflate::new(true, 15);
2480+
2481+
let compressed = [0x08, 0xb8, 0x0, 0x0, 0x0, 0x1];
2482+
let mut decompressed = [0u8; 64];
2483+
2484+
let ret = inflate.decompress(
2485+
&compressed,
2486+
&mut decompressed,
2487+
zlib_rs::InflateFlush::Finish,
2488+
);
2489+
2490+
assert_eq!(ret, Err(InflateError::NeedDict { dict_id: 1 }));
2491+
}
2492+
2493+
#[test]
2494+
fn reset_reuse() {
2495+
let input1 = "Hello World!";
2496+
let input2 = "Goodbye World!";
2497+
2498+
let mut output1 = [0u8; 64];
2499+
let config1 = DeflateConfig {
2500+
window_bits: 15,
2501+
..DeflateConfig::default()
2502+
};
2503+
let (compressed1, ret) = compress_slice(&mut output1, input1.as_bytes(), config1);
2504+
assert_eq!(ret, ReturnCode::Ok);
2505+
2506+
let mut output2 = [0u8; 64];
2507+
let config2 = DeflateConfig {
2508+
window_bits: -15,
2509+
..DeflateConfig::default()
2510+
};
2511+
let (compressed2, ret) = compress_slice(&mut output2, input2.as_bytes(), config2);
2512+
assert_eq!(ret, ReturnCode::Ok);
2513+
2514+
// Start with header enabled.
2515+
let zlib_header_first = true;
2516+
let mut inflate = zlib_rs::Inflate::new(zlib_header_first, 15);
2517+
2518+
let mut decompressed1 = [0u8; 64];
2519+
inflate
2520+
.decompress(
2521+
compressed1,
2522+
&mut decompressed1,
2523+
zlib_rs::InflateFlush::Finish,
2524+
)
2525+
.unwrap();
2526+
2527+
assert_eq!(inflate.total_in() as usize, compressed1.len());
2528+
assert_eq!(
2529+
&decompressed1[..inflate.total_out() as usize],
2530+
input1.as_bytes()
2531+
);
2532+
2533+
// Reset for a *raw* stream: swap the zlib_header flag.
2534+
inflate.reset(!zlib_header_first);
2535+
2536+
let mut decompressed2 = [0u8; 64];
2537+
inflate
2538+
.decompress(
2539+
compressed2,
2540+
&mut decompressed2,
2541+
zlib_rs::InflateFlush::Finish,
2542+
)
2543+
.unwrap();
2544+
2545+
assert_eq!(inflate.total_in() as usize, compressed2.len());
2546+
assert_eq!(
2547+
&decompressed2[..inflate.total_out() as usize],
2548+
input2.as_bytes()
2549+
);
2550+
}
2551+
}

zlib-rs/src/deflate.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,27 @@ impl<'a> DeflateStream<'a> {
121121
self.state.bit_writer.bits_used,
122122
)
123123
}
124+
125+
pub fn new(level: i32, zlib_header: bool, window_bits: u8) -> Self {
126+
let mut inner = crate::c_api::z_stream::default();
127+
128+
let config = DeflateConfig {
129+
window_bits: if zlib_header {
130+
i32::from(window_bits)
131+
} else {
132+
-i32::from(window_bits)
133+
},
134+
level,
135+
method: Method::Deflated,
136+
mem_level: DEF_MEM_LEVEL,
137+
strategy: Strategy::Default,
138+
};
139+
140+
let ret = crate::deflate::init(&mut inner, config);
141+
assert_eq!(ret, ReturnCode::Ok);
142+
143+
unsafe { core::mem::transmute(inner) }
144+
}
124145
}
125146

126147
/// number of elements in hash table

zlib-rs/src/inflate.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,23 @@ impl<'a> InflateStream<'a> {
126126
// safety: a valid &mut InflateStream is also a valid &mut z_stream
127127
unsafe { &mut *(self as *mut _ as *mut z_stream) }
128128
}
129+
130+
pub fn new(zlib_header: bool, window_bits: u8) -> Self {
131+
let mut inner = crate::c_api::z_stream::default();
132+
133+
let config = InflateConfig {
134+
window_bits: if zlib_header {
135+
i32::from(window_bits)
136+
} else {
137+
-i32::from(window_bits)
138+
},
139+
};
140+
141+
let ret = crate::inflate::init(&mut inner, config);
142+
assert_eq!(ret, ReturnCode::Ok);
143+
144+
unsafe { core::mem::transmute(inner) }
145+
}
129146
}
130147

131148
const MAX_BITS: u8 = 15; // maximum number of bits in a code
@@ -2616,7 +2633,7 @@ pub fn set_dictionary(stream: &mut InflateStream, dictionary: &[u8]) -> ReturnCo
26162633
ReturnCode::Ok
26172634
}
26182635

2619-
pub fn end<'a>(stream: &'a mut InflateStream<'a>) -> &'a mut z_stream {
2636+
pub fn end<'a>(stream: &'a mut InflateStream<'_>) -> &'a mut z_stream {
26202637
let alloc = stream.alloc;
26212638

26222639
let mut window = Window::empty();

zlib-rs/src/lib.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
#[cfg(any(feature = "rust-allocator", feature = "c-allocator"))]
55
extern crate alloc;
66

7-
mod adler32;
87
pub mod allocate;
98
pub mod c_api;
10-
mod cpu_features;
119
pub mod crc32;
1210
pub mod deflate;
1311
pub mod inflate;
12+
13+
mod adler32;
14+
mod cpu_features;
15+
mod stable;
1416
mod weak_slice;
1517

18+
pub use stable::{Deflate, DeflateError, Inflate, InflateError, Status};
19+
1620
pub use adler32::{adler32, adler32_combine};
1721
pub use crc32::{crc32, crc32_combine, get_crc_table};
1822

@@ -188,7 +192,11 @@ impl From<i32> for ReturnCode {
188192
}
189193

190194
impl ReturnCode {
191-
const fn error_message_str(self) -> &'static str {
195+
fn error_message_str(self) -> &'static str {
196+
self.error_message_str_with_null().trim_end_matches('\0')
197+
}
198+
199+
const fn error_message_str_with_null(self) -> &'static str {
192200
match self {
193201
ReturnCode::Ok => "\0",
194202
ReturnCode::StreamEnd => "stream end\0",
@@ -203,7 +211,7 @@ impl ReturnCode {
203211
}
204212

205213
pub const fn error_message(self) -> *const core::ffi::c_char {
206-
let msg = self.error_message_str();
214+
let msg = self.error_message_str_with_null();
207215
msg.as_ptr().cast::<core::ffi::c_char>()
208216
}
209217

0 commit comments

Comments
 (0)