use super::StreamingDecoder;
use crate::io::Read;
fn frame_declaring(fcs: u64, single_segment: bool, payload: &[u8]) -> alloc::vec::Vec<u8> {
let mut f = alloc::vec![0x28, 0xB5, 0x2F, 0xFD];
if single_segment {
f.push(0xE0);
} else {
f.extend_from_slice(&[0xC0, 0x00]);
}
f.extend_from_slice(&fcs.to_le_bytes());
let header = ((payload.len() as u32) << 3) | 1;
f.extend_from_slice(&header.to_le_bytes()[..3]);
f.extend_from_slice(payload);
f
}
fn drain_with_read(
mut decoder: StreamingDecoder<&[u8], crate::decoding::FrameDecoder>,
) -> Result<alloc::vec::Vec<u8>, crate::io::Error> {
let mut out = alloc::vec::Vec::new();
let mut buf = [0u8; 4096];
loop {
let n = decoder.read(&mut buf)?;
if n == 0 {
return Ok(out);
}
out.extend_from_slice(&buf[..n]);
}
}
fn frame_of(payload: &[u8]) -> alloc::vec::Vec<u8> {
crate::encoding::compress_to_vec(payload, crate::encoding::CompressionLevel::Fastest)
}
fn skippable_frame(payload: &[u8]) -> alloc::vec::Vec<u8> {
let mut f = alloc::vec![0x50, 0x2A, 0x4D, 0x18];
f.extend_from_slice(&(payload.len() as u32).to_le_bytes());
f.extend_from_slice(payload);
f
}
#[test]
fn read_continues_into_following_frames() {
let mut stream = frame_of(b"first frame, ");
stream.extend(frame_of(b"second frame, "));
stream.extend(frame_of(b"third"));
let decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
assert_eq!(
drain_with_read(decoder).unwrap(),
b"first frame, second frame, third"
);
}
#[test]
fn read_skips_skippable_frames_anywhere_in_the_stream() {
let mut stream = skippable_frame(b"leading metadata");
stream.extend(frame_of(b"one "));
stream.extend(skippable_frame(b""));
stream.extend(frame_of(b"two"));
stream.extend(skippable_frame(b"trailer"));
let decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
assert_eq!(drain_with_read(decoder).unwrap(), b"one two");
}
#[test]
fn a_stream_of_only_skippable_frames_decodes_to_nothing() {
let mut stream = skippable_frame(b"first");
stream.extend(skippable_frame(b"second"));
let decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
assert!(drain_with_read(decoder).unwrap().is_empty());
let mut decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
let mut out = alloc::vec::Vec::new();
decoder.read_to_end(&mut out).unwrap();
assert!(out.is_empty());
}
#[test]
fn read_rejects_bytes_after_the_last_frame_that_are_not_a_frame() {
let mut stream = frame_of(b"payload");
stream.extend_from_slice(b"not a frame");
let decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
assert!(drain_with_read(decoder).is_err());
}
#[test]
fn read_applies_the_constructor_dictionary_to_following_frames() {
use crate::decoding::{Dictionary, DictionaryHandle};
use crate::encoding::{CompressionLevel, FrameCompressor};
let content = b"shared words the frames reuse again and again".to_vec();
let handle = DictionaryHandle::from_dictionary(
Dictionary::from_raw_content(7, content.clone()).unwrap(),
);
let mut compressor: FrameCompressor = FrameCompressor::new(CompressionLevel::Default);
compressor
.set_dictionary(Dictionary::from_raw_content(7, content).unwrap())
.unwrap();
let mut stream = compressor.compress_independent_frame(b"the frames reuse words");
stream.extend(compressor.compress_independent_frame(b" and again the shared words"));
let decoder = StreamingDecoder::new_with_dictionary_handle(stream.as_slice(), &handle).unwrap();
assert_eq!(
drain_with_read(decoder).unwrap(),
b"the frames reuse words and again the shared words"
);
}
struct OpenPipe<'a> {
data: &'a [u8],
}
impl Read for OpenPipe<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, crate::io::Error> {
if self.data.is_empty() {
return Err(crate::io::Error::from(crate::io::ErrorKind::WouldBlock));
}
self.data.read(buf)
}
}
struct Trickle<'a> {
data: &'a [u8],
block_next: bool,
}
impl Read for Trickle<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, crate::io::Error> {
self.block_next = !self.block_next;
if !self.block_next {
return Err(crate::io::Error::from(crate::io::ErrorKind::WouldBlock));
}
let n = buf.len().min(1).min(self.data.len());
buf[..n].copy_from_slice(&self.data[..n]);
self.data = &self.data[n..];
Ok(n)
}
}
#[test]
fn read_resumes_wherever_a_would_block_interrupts_it() {
let payload: alloc::vec::Vec<u8> = (0..300_000u32).map(|i| (i % 251) as u8).collect();
let mut compressor =
crate::encoding::FrameCompressor::new(crate::encoding::CompressionLevel::Fastest);
compressor.set_content_checksum(true);
compressor.set_source(payload.as_slice());
let mut stream = alloc::vec::Vec::new();
compressor.set_drain(&mut stream);
compressor.compress();
stream.extend(skippable_frame(b"metadata between frames"));
stream.extend(frame_of(b"second"));
let descriptor = crate::decoding::frame::FrameDescriptor(stream[4]);
let first_header = 5
+ usize::from(!descriptor.single_segment_flag())
+ usize::from(descriptor.dictionary_id_bytes().unwrap())
+ usize::from(descriptor.frame_content_size_bytes().unwrap());
struct Staged<'a> {
head: &'a [u8],
tail: Trickle<'a>,
}
impl Read for Staged<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, crate::io::Error> {
if self.head.is_empty() {
self.tail.read(buf)
} else {
self.head.read(buf)
}
}
}
let staged = Staged {
head: &stream[..first_header],
tail: Trickle {
data: &stream[first_header..],
block_next: false,
},
};
let mut decoder = StreamingDecoder::new(staged).unwrap();
#[cfg(feature = "hash")]
decoder
.decoder_mut()
.set_content_checksum(crate::decoding::ContentChecksum::Verify);
let mut out = alloc::vec::Vec::new();
let mut buf = [0u8; 4096];
loop {
match decoder.read(&mut buf) {
Ok(0) => break,
Ok(n) => out.extend_from_slice(&buf[..n]),
Err(e) if e.kind() == crate::io::ErrorKind::WouldBlock => {}
Err(e) => panic!("the stream must survive WouldBlock: {e:?}"),
}
}
let mut expected = payload;
expected.extend_from_slice(b"second");
assert!(out == expected, "decoded {} bytes", out.len());
}
#[test]
fn read_continues_into_following_magicless_frames() {
let first = frame_declaring(5, false, b"first");
let second = frame_declaring(6, false, b"second");
let mut stream = first[4..].to_vec();
let first_len = stream.len();
stream.extend_from_slice(&second[4..]);
struct Staged<'a> {
head: &'a [u8],
tail: Trickle<'a>,
}
impl Read for Staged<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, crate::io::Error> {
if self.head.is_empty() {
self.tail.read(buf)
} else {
self.head.read(buf)
}
}
}
let mut frame_decoder = crate::decoding::FrameDecoder::new();
frame_decoder.set_magicless(true);
let staged = Staged {
head: &stream[..first_len],
tail: Trickle {
data: &stream[first_len..],
block_next: false,
},
};
let mut decoder = StreamingDecoder::new_with_decoder(staged, frame_decoder).unwrap();
let mut out = alloc::vec::Vec::new();
let mut buf = [0u8; 64];
loop {
match decoder.read(&mut buf) {
Ok(0) => break,
Ok(n) => out.extend_from_slice(&buf[..n]),
Err(e) if e.kind() == crate::io::ErrorKind::WouldBlock => {}
Err(e) => panic!("magicless frames must decode in turn: {e:?}"),
}
}
assert_eq!(out, b"firstsecond");
}
#[test]
fn into_inner_after_a_frame_returns_the_rest_of_the_source() {
let payload = b"one frame of content";
let mut stream = frame_of(payload);
stream.extend_from_slice(b"bytes after the frame");
let mut decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
let mut out = [0u8; 20];
decoder.read_exact(&mut out).unwrap();
assert_eq!(&out, payload);
assert_eq!(decoder.into_inner(), b"bytes after the frame");
}
#[test]
fn into_inner_after_a_checksummed_frame_returns_the_rest_of_the_source() {
let payload = b"one frame of content";
let mut compressor =
crate::encoding::FrameCompressor::new(crate::encoding::CompressionLevel::Fastest);
compressor.set_content_checksum(true);
compressor.set_source(&payload[..]);
let mut stream = alloc::vec::Vec::new();
compressor.set_drain(&mut stream);
compressor.compress();
stream.extend_from_slice(b"bytes after the frame");
let mut decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
let mut out = [0u8; 20];
decoder.read_exact(&mut out).unwrap();
assert_eq!(&out, payload);
assert_eq!(decoder.into_inner(), b"bytes after the frame");
}
#[test]
fn a_small_frame_buffers_no_more_than_its_block() {
let payload: alloc::vec::Vec<u8> = (0..1000u32)
.map(|i| (i.wrapping_mul(2654435761) >> 24) as u8)
.collect();
let frame = frame_of(&payload);
let mut decoder = StreamingDecoder::new(frame.as_slice()).unwrap();
let mut out = alloc::vec::Vec::new();
let mut buf = [0u8; 256];
loop {
let n = decoder.read(&mut buf).unwrap();
if n == 0 {
break;
}
out.extend_from_slice(&buf[..n]);
}
assert_eq!(out, payload);
assert!(
decoder.input.buf.len() <= frame.len(),
"a {}-byte frame used a {}-byte input buffer",
frame.len(),
decoder.input.buf.len()
);
}
struct Interrupting<'a> {
data: &'a [u8],
interrupt_next: bool,
}
impl Read for Interrupting<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, crate::io::Error> {
self.interrupt_next = !self.interrupt_next;
if self.interrupt_next {
return Err(crate::io::Error::from(crate::io::ErrorKind::Interrupted));
}
self.data.read(buf)
}
}
#[test]
fn read_retries_an_interrupted_source_everywhere() {
let mut stream = skippable_frame(b"leading");
stream.extend(frame_of(b"one "));
stream.extend(skippable_frame(b"between"));
stream.extend(frame_of(b"two"));
let source = Interrupting {
data: &stream,
interrupt_next: false,
};
let mut decoder = StreamingDecoder::new(source).unwrap();
let mut out = alloc::vec::Vec::new();
let mut buf = [0u8; 64];
loop {
let n = decoder.read(&mut buf).unwrap();
if n == 0 {
break;
}
out.extend_from_slice(&buf[..n]);
}
assert_eq!(out, b"one two");
}
#[test]
fn a_truncated_skippable_frame_is_an_error() {
let mut leading = skippable_frame(b"metadata");
leading.truncate(leading.len() - 3);
let err = StreamingDecoder::new(leading.as_slice())
.err()
.expect("a leading skippable frame cut short must not start the stream");
assert!(
matches!(
err,
crate::decoding::errors::FrameDecoderError::FailedToSkipFrame
),
"{err:?}"
);
let mut trailing = frame_of(b"payload");
let mut skippable = skippable_frame(b"metadata");
skippable.truncate(skippable.len() - 3);
trailing.extend(skippable);
let decoder = StreamingDecoder::new(trailing.as_slice()).unwrap();
let err =
drain_with_read(decoder).expect_err("a skippable frame cut short must not end cleanly");
assert!(
alloc::format!("{err:?}").contains("FailedToSkipFrame"),
"{err:?}"
);
}
#[test]
fn a_source_error_around_a_leading_skippable_frame_fails_the_constructor() {
use crate::decoding::errors::{FrameDecoderError, ReadFrameHeaderError};
struct FailsAfter<'a> {
data: &'a [u8],
}
impl Read for FailsAfter<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, crate::io::Error> {
if self.data.is_empty() {
return Err(crate::io::Error::from(crate::io::ErrorKind::Other));
}
self.data.read(buf)
}
}
let skippable = skippable_frame(b"metadata");
let inside = FailsAfter {
data: &skippable[..skippable.len() - 3],
};
let err = StreamingDecoder::new(inside)
.err()
.expect("a source error inside a skippable frame must fail");
let shown = alloc::format!("{err:?}");
assert!(
shown.contains("FailedToReadSkippableFrame") && shown.contains("Other"),
"{shown}"
);
let at_boundary = FailsAfter { data: &skippable };
let err = StreamingDecoder::new(at_boundary)
.err()
.expect("a source error at the next frame boundary must fail");
assert!(
matches!(
err,
FrameDecoderError::ReadFrameHeaderError(ReadFrameHeaderError::MagicNumberReadError(_))
),
"{err:?}"
);
}
#[test]
fn read_rejects_a_stream_cut_inside_the_next_frame_header() {
let mut stream = frame_of(b"payload");
let next = frame_of(b"next");
stream.extend_from_slice(&next[..5]);
let decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
assert!(drain_with_read(decoder).is_err());
}
#[test]
fn read_rejects_a_block_that_does_not_decode() {
let frame = [
0x28, 0xB5, 0x2F, 0xFD, 0x20, 16, 0x25, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
];
let decoder = StreamingDecoder::new(&frame[..]).unwrap();
assert!(drain_with_read(decoder).is_err());
}
#[test]
fn read_rejects_a_reserved_block_without_waiting_for_its_body() {
let header = (10u32 << 3) | (3 << 1) | 1;
let mut frame = alloc::vec![0x28, 0xB5, 0x2F, 0xFD, 0x20, 16];
frame.extend_from_slice(&header.to_le_bytes()[..3]);
let mut decoder = StreamingDecoder::new(OpenPipe { data: &frame }).unwrap();
let mut buf = [0u8; 64];
let err = decoder
.read(&mut buf)
.expect_err("a reserved block type must fail");
assert_ne!(err.kind(), crate::io::ErrorKind::WouldBlock, "{err:?}");
}
#[test]
fn read_rejects_an_oversized_rle_block_without_waiting_for_its_byte() {
let header = (100u32 << 3) | (1 << 1) | 1;
let mut frame = alloc::vec![0x28, 0xB5, 0x2F, 0xFD, 0x20, 16];
frame.extend_from_slice(&header.to_le_bytes()[..3]);
let mut decoder = StreamingDecoder::new(OpenPipe { data: &frame }).unwrap();
let mut buf = [0u8; 64];
let err = decoder
.read(&mut buf)
.expect_err("an RLE block over the block maximum must fail");
assert_ne!(err.kind(), crate::io::ErrorKind::WouldBlock, "{err:?}");
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_releases_the_compressed_stream() {
let payload: alloc::vec::Vec<u8> = (0..50_000u32)
.map(|i| (i.wrapping_mul(2654435761) >> 24) as u8)
.collect();
let stream = frame_of(&payload);
let mut decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
let mut out = alloc::vec::Vec::new();
decoder.read_to_end(&mut out).unwrap();
assert_eq!(out, payload);
assert_eq!(decoder.input.buf.capacity(), 0);
}
struct Counting<'a> {
data: &'a [u8],
reads: usize,
}
impl Read for Counting<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, crate::io::Error> {
self.reads += 1;
self.data.read(buf)
}
}
#[test]
fn a_leading_skippable_frame_is_skipped_in_large_chunks() {
let mut stream = skippable_frame(&alloc::vec![0u8; 64 * 1024]);
stream.extend(frame_of(b"after the metadata"));
let mut source = Counting {
data: &stream,
reads: 0,
};
StreamingDecoder::new(&mut source).unwrap();
let chunks = 64 * 1024 / super::SKIP_CHUNK as usize;
assert!(
source.reads <= chunks + 8,
"{} reads for a 64 KiB skippable frame",
source.reads
);
}
fn incompressible(len: usize) -> alloc::vec::Vec<u8> {
let mut x = 0x9E37_79B9_7F4A_7C15u64;
(0..len)
.map(|_| {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
(x >> 32) as u8
})
.collect()
}
#[test]
fn read_resumes_raw_blocks_wherever_a_would_block_interrupts_them() {
let payload = incompressible(300_000);
let mut compressor =
crate::encoding::FrameCompressor::new(crate::encoding::CompressionLevel::Fastest);
compressor.set_content_checksum(true);
compressor.set_source(payload.as_slice());
let mut stream = alloc::vec::Vec::new();
compressor.set_drain(&mut stream);
compressor.compress();
assert!(
stream.len() > payload.len(),
"the frame must hold Raw blocks"
);
stream.extend(frame_of(b"next"));
let descriptor = crate::decoding::frame::FrameDescriptor(stream[4]);
let first_header = 5
+ usize::from(!descriptor.single_segment_flag())
+ usize::from(descriptor.dictionary_id_bytes().unwrap())
+ usize::from(descriptor.frame_content_size_bytes().unwrap());
let stops: alloc::vec::Vec<usize> = (first_header..stream.len()).collect();
let source = Arrivals {
data: &stream,
served: 0,
stops: &stops,
};
let mut decoder = StreamingDecoder::new(source).unwrap();
#[cfg(feature = "hash")]
decoder
.decoder_mut()
.set_content_checksum(crate::decoding::ContentChecksum::Verify);
let mut out = alloc::vec::Vec::new();
let mut buf = [0u8; 4096];
loop {
match decoder.read(&mut buf) {
Ok(0) => break,
Ok(n) => out.extend_from_slice(&buf[..n]),
Err(e) if e.kind() == crate::io::ErrorKind::WouldBlock => {}
Err(e) => panic!("Raw blocks must survive WouldBlock: {e:?}"),
}
}
let mut expected = payload;
expected.extend_from_slice(b"next");
assert!(out == expected, "decoded {} bytes", out.len());
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_after_a_part_read_raw_block_is_complete() {
let payload = incompressible(20_000);
let frame = frame_of(&payload);
let descriptor = crate::decoding::frame::FrameDescriptor(frame[4]);
let header = 5
+ usize::from(!descriptor.single_segment_flag())
+ usize::from(descriptor.dictionary_id_bytes().unwrap())
+ usize::from(descriptor.frame_content_size_bytes().unwrap());
let stops = [header + 3 + 100];
let source = Arrivals {
data: &frame,
served: 0,
stops: &stops,
};
let mut decoder = StreamingDecoder::new(source).unwrap();
let mut buf = [0u8; 64];
loop {
match decoder.read(&mut buf) {
Err(e) if e.kind() == crate::io::ErrorKind::WouldBlock => break,
Ok(n) => assert_eq!(n, 0, "a frame's window holds its output until it ends"),
Err(e) => panic!("{e:?}"),
}
}
let mut out = alloc::vec::Vec::new();
decoder.read_to_end(&mut out).unwrap();
assert!(out == payload, "decoded {} bytes", out.len());
}
#[test]
fn read_rejects_a_frame_cut_inside_a_raw_block() {
let payload = incompressible(20_000);
let mut frame = frame_of(&payload);
frame.truncate(frame.len() - 100);
let decoder = StreamingDecoder::new(frame.as_slice()).unwrap();
let err = drain_with_read(decoder).expect_err("a cut Raw block must fail");
assert!(
alloc::format!("{err:?}").contains("UnexpectedEof"),
"{err:?}"
);
}
#[cfg(all(feature = "lsm", feature = "hash"))]
#[test]
fn read_records_per_block_checksums_for_every_block() {
let mut payload = b"compressible line of text, again and again\n".repeat(8_000);
payload.extend(incompressible(300_000));
let frame = crate::encoding::compress_to_vec(
payload.as_slice(),
crate::encoding::CompressionLevel::Fastest,
);
let mut whole = crate::decoding::FrameDecoder::new();
whole.enable_per_block_checksums();
let mut out = alloc::vec![0u8; payload.len()];
whole.decode_all(&frame, &mut out).unwrap();
let expected = whole.computed_block_checksums().to_vec();
assert!(expected.len() > 2, "the frame must hold several blocks");
let mut frame_decoder = crate::decoding::FrameDecoder::new();
frame_decoder.enable_per_block_checksums();
let mut decoder = StreamingDecoder::new_with_decoder(frame.as_slice(), frame_decoder).unwrap();
let mut decoded = alloc::vec::Vec::new();
let mut buf = [0u8; 4096];
loop {
let n = decoder.read(&mut buf).unwrap();
if n == 0 {
break;
}
decoded.extend_from_slice(&buf[..n]);
}
assert!(decoded == payload);
assert_eq!(
decoder.decoder.computed_block_checksums(),
expected.as_slice()
);
}
struct Arrivals<'a> {
data: &'a [u8],
served: usize,
stops: &'a [usize],
}
impl Read for Arrivals<'_> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, crate::io::Error> {
let limit = match self.stops.first() {
Some(&stop) if stop == self.served => {
self.stops = &self.stops[1..];
return Err(crate::io::Error::from(crate::io::ErrorKind::WouldBlock));
}
Some(&stop) => stop,
None => self.data.len(),
};
let n = buf.len().min(limit - self.served);
buf[..n].copy_from_slice(&self.data[self.served..self.served + n]);
self.served += n;
Ok(n)
}
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_resumes_after_the_source_would_block() {
let payload: alloc::vec::Vec<u8> = (0..20_000u32).map(|i| (i % 251) as u8).collect();
let frame = frame_of(&payload);
let descriptor = crate::decoding::frame::FrameDescriptor(frame[4]);
let header = 5
+ usize::from(!descriptor.single_segment_flag())
+ usize::from(descriptor.dictionary_id_bytes().unwrap())
+ usize::from(descriptor.frame_content_size_bytes().unwrap());
let stops = [header + 10, header + 50];
let source = Arrivals {
data: &frame,
served: 0,
stops: &stops,
};
let mut decoder = StreamingDecoder::new(source).unwrap();
let mut buf = [0u8; 64];
let err = decoder
.read(&mut buf)
.expect_err("the first block is not all here");
assert_eq!(err.kind(), crate::io::ErrorKind::WouldBlock);
let mut out = alloc::vec::Vec::new();
let err = decoder
.read_to_end(&mut out)
.expect_err("the source stops again");
assert_eq!(err.kind(), crate::io::ErrorKind::WouldBlock);
out.clear();
decoder.read_to_end(&mut out).unwrap();
assert!(out == payload, "decoded {} bytes", out.len());
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_returns_the_source_error() {
let frame = frame_of(b"complete frame");
let mut decoder = StreamingDecoder::new(OpenPipe { data: &frame }).unwrap();
let mut out = alloc::vec::Vec::new();
let err = decoder
.read_to_end(&mut out)
.expect_err("a source that would block cannot be read to its end");
assert_eq!(err.kind(), crate::io::ErrorKind::WouldBlock);
}
#[test]
fn read_rejects_a_frame_cut_inside_its_checksum() {
let mut compressor =
crate::encoding::FrameCompressor::new(crate::encoding::CompressionLevel::Fastest);
compressor.set_content_checksum(true);
compressor.set_source(&b"checksummed payload"[..]);
let mut frame = alloc::vec::Vec::new();
compressor.set_drain(&mut frame);
compressor.compress();
frame.truncate(frame.len() - 2);
let decoder = StreamingDecoder::new(frame.as_slice()).unwrap();
let err = drain_with_read(decoder).expect_err("a frame missing checksum bytes must fail");
assert!(
alloc::format!("{err:?}").contains("FailedToReadChecksum"),
"{err:?}"
);
}
#[test]
fn a_dictionary_constructor_rejects_bytes_that_are_not_a_frame() {
use crate::decoding::{Dictionary, DictionaryHandle};
let handle = DictionaryHandle::from_dictionary(
Dictionary::from_raw_content(7, b"dictionary content".to_vec()).unwrap(),
);
assert!(StreamingDecoder::new_with_dictionary_handle(&b"not a frame"[..], &handle).is_err());
}
#[test]
fn read_delivers_a_finished_frame_before_the_next_frame_arrives() {
let frame = frame_of(b"complete frame");
let mut decoder = StreamingDecoder::new(OpenPipe { data: &frame }).unwrap();
let mut buf = [0u8; 64];
let n = decoder.read(&mut buf).unwrap();
assert_eq!(&buf[..n], b"complete frame");
}
#[test]
fn read_rejects_a_frame_shorter_than_its_declared_size() {
for single_segment in [false, true] {
let frame = frame_declaring(64 << 20, single_segment, &[]);
let decoder = StreamingDecoder::new(frame.as_slice()).unwrap();
let err = drain_with_read(decoder)
.expect_err("a frame that produced less than it declared must not end cleanly");
assert!(
alloc::format!("{err:?}").contains("FrameContentSizeMismatch"),
"single_segment={single_segment}: {err:?}"
);
}
}
#[cfg(feature = "std")]
#[test]
fn read_after_a_direct_read_to_end_ends_cleanly() {
use crate::encoding::{CompressionLevel, compress_to_vec};
let payload: alloc::vec::Vec<u8> = (0..20_000u32).map(|i| (i % 251) as u8).collect();
let frame = compress_to_vec(payload.as_slice(), CompressionLevel::Fastest);
let mut decoder = StreamingDecoder::new(frame.as_slice()).unwrap();
let mut out = alloc::vec::Vec::new();
decoder.read_to_end(&mut out).unwrap();
assert_eq!(out, payload);
let mut buf = [0u8; 16];
assert_eq!(decoder.read(&mut buf).unwrap(), 0);
}
#[test]
fn read_rejects_a_frame_longer_than_its_declared_size() {
let frame = frame_declaring(1, false, b"abcd");
let decoder = StreamingDecoder::new(frame.as_slice()).unwrap();
let err = drain_with_read(decoder)
.expect_err("a frame that produced more than it declared must not end cleanly");
assert!(
alloc::format!("{err:?}").contains("FrameContentSizeMismatch"),
"{err:?}"
);
}
#[cfg(feature = "hash")]
#[test]
fn read_delivering_bytes_defers_checksum_error_to_next_call() {
use crate::decoding::ContentChecksum;
use crate::encoding::{CompressionLevel, FrameCompressor};
use crate::io::ErrorKind;
use alloc::vec;
use alloc::vec::Vec;
let payload: Vec<u8> = (0..8192u32).map(|i| (i & 0xFF) as u8).collect();
let mut compressor = FrameCompressor::new(CompressionLevel::Default);
compressor.set_content_checksum(true);
compressor.set_source(payload.as_slice());
let mut compressed = Vec::new();
compressor.set_drain(&mut compressed);
compressor.compress();
let last = compressed.len() - 1;
compressed[last] ^= 0xFF;
let mut decoder = StreamingDecoder::new(compressed.as_slice()).unwrap();
decoder
.decoder
.set_content_checksum(ContentChecksum::Verify);
let mut buf = vec![0u8; payload.len() + 4096];
let n = decoder
.read(&mut buf)
.expect("a read that delivered bytes must not return the checksum Err");
assert_eq!(n, payload.len());
assert_eq!(&buf[..n], payload.as_slice());
let err = decoder
.read(&mut buf)
.expect_err("deferred checksum mismatch must surface on the terminating read");
assert_eq!(err.kind(), ErrorKind::Other);
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_decode_in_place_matches_and_takes_direct_path() {
use crate::encoding::{CompressionLevel, FrameCompressor};
use alloc::vec::Vec;
let payload: Vec<u8> = (0..20_000u32)
.map(|i| (i.wrapping_mul(2654435761) >> 24) as u8)
.collect();
let mut compressor = FrameCompressor::new(CompressionLevel::Default);
compressor.set_source(payload.as_slice());
let mut compressed = Vec::new();
compressor.set_drain(&mut compressed);
compressor.compress();
let mut decoder = StreamingDecoder::new(compressed.as_slice()).unwrap();
let mut out = Vec::new();
let n = decoder.read_to_end(&mut out).unwrap();
assert_eq!(n, payload.len());
assert_eq!(out, payload);
assert_eq!(decoder.decoder.direct_frames(), 1);
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_after_partial_read_is_complete() {
use crate::encoding::{CompressionLevel, FrameCompressor};
use alloc::vec;
use alloc::vec::Vec;
let payload: Vec<u8> = (0..20_000u32).map(|i| (i & 0xFF) as u8).collect();
let mut compressor = FrameCompressor::new(CompressionLevel::Default);
compressor.set_source(payload.as_slice());
let mut compressed = Vec::new();
compressor.set_drain(&mut compressed);
compressor.compress();
let mut decoder = StreamingDecoder::new(compressed.as_slice()).unwrap();
let mut head = vec![0u8; 4096];
let got = decoder.read(&mut head).unwrap();
assert!(got > 0 && got <= head.len());
let mut out = Vec::new();
out.extend_from_slice(&head[..got]);
decoder.read_to_end(&mut out).unwrap();
assert_eq!(out, payload);
assert_eq!(decoder.decoder.direct_frames(), 0);
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_decodes_all_concatenated_frames() {
use crate::encoding::{CompressionLevel, compress_slice_to_vec};
use alloc::vec::Vec;
let a: Vec<u8> = (0..5000u32).map(|i| (i & 0xFF) as u8).collect();
let b: Vec<u8> = (0..3000u32)
.map(|i| ((i.wrapping_mul(7)) & 0xFF) as u8)
.collect();
let mut stream = compress_slice_to_vec(&a, CompressionLevel::Level(3));
stream.extend_from_slice(&compress_slice_to_vec(&b, CompressionLevel::Level(3)));
let mut decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
let mut out = Vec::new();
decoder.read_to_end(&mut out).unwrap();
let mut expected = a.clone();
expected.extend_from_slice(&b);
assert_eq!(out, expected);
assert_eq!(decoder.decoder.direct_frames(), 2);
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_after_partial_read_decodes_all_concatenated_frames() {
use crate::encoding::{CompressionLevel, compress_slice_to_vec};
use alloc::vec;
use alloc::vec::Vec;
let a: Vec<u8> = (0..6000u32).map(|i| (i & 0xFF) as u8).collect();
let b: Vec<u8> = (0..4000u32)
.map(|i| ((i.wrapping_mul(11)) & 0xFF) as u8)
.collect();
let mut stream = compress_slice_to_vec(&a, CompressionLevel::Level(3));
stream.extend_from_slice(&compress_slice_to_vec(&b, CompressionLevel::Level(3)));
let mut decoder = StreamingDecoder::new(stream.as_slice()).unwrap();
let mut head = vec![0u8; 2048];
let got = decoder.read(&mut head).unwrap();
assert!(got > 0 && got <= head.len());
let mut out = Vec::new();
out.extend_from_slice(&head[..got]);
decoder.read_to_end(&mut out).unwrap();
let mut expected = a.clone();
expected.extend_from_slice(&b);
assert_eq!(
out, expected,
"fallback path must decode frame 2 too, not stop at frame 1 EOF"
);
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_concatenated_dict_frames_decode_with_dictionary() {
use crate::encoding::{CompressionLevel, FrameCompressor};
use alloc::vec::Vec;
let dict_raw = include_bytes!("../../../dict_tests/dictionary");
let compress_with_dict = |payload: &[u8]| -> Vec<u8> {
let mut compressor = FrameCompressor::new(CompressionLevel::Default);
compressor
.set_dictionary_from_bytes(dict_raw)
.expect("dict load");
compressor.set_source(payload);
let mut compressed = Vec::new();
compressor.set_drain(&mut compressed);
compressor.compress();
compressed
};
let a = b"first dictionary-compressed frame payload".to_vec();
let b = b"second dictionary-compressed frame payload".to_vec();
let mut stream = compress_with_dict(&a);
stream.extend_from_slice(&compress_with_dict(&b));
let mut decoder =
StreamingDecoder::new_with_dictionary_bytes(stream.as_slice(), dict_raw).unwrap();
let mut out = Vec::new();
decoder
.read_to_end(&mut out)
.expect("both dict frames must decode with the forced dictionary");
let mut expected = a.clone();
expected.extend_from_slice(&b);
assert_eq!(out, expected);
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_truncates_output_on_direct_decode_error() {
use crate::encoding::{CompressionLevel, FrameCompressor};
use alloc::vec::Vec;
let payload: Vec<u8> = (0..5000u32).map(|i| (i & 0xFF) as u8).collect();
let mut compressor = FrameCompressor::new(CompressionLevel::Default);
compressor.set_source(payload.as_slice());
let mut compressed = Vec::new();
compressor.set_drain(&mut compressed);
compressor.compress();
compressed.truncate(compressed.len() - 40);
let mut decoder = StreamingDecoder::new(compressed.as_slice()).unwrap();
let mut out = b"SENTINEL".to_vec();
let result = decoder.read_to_end(&mut out);
assert!(result.is_err(), "truncated block must fail the decode");
assert_eq!(
out, b"SENTINEL",
"failed direct decode must not append non-decoded bytes to output"
);
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_truncates_output_on_midframe_fallback_error() {
use crate::encoding::{CompressionLevel, CompressionParameters, FrameCompressor};
use alloc::vec;
use alloc::vec::Vec;
let payload: Vec<u8> = (0..320_000u32)
.map(|i| (i.wrapping_mul(2654435761) >> 24) as u8)
.collect();
let params = CompressionParameters::builder(CompressionLevel::Default)
.window_log(17)
.build()
.expect("window_log within bounds");
let mut compressor = FrameCompressor::new(CompressionLevel::Default);
compressor.set_parameters(¶ms);
compressor.set_source(payload.as_slice());
let mut compressed = Vec::new();
compressor.set_drain(&mut compressed);
compressor.compress();
compressed.truncate(compressed.len() - 40);
let mut decoder = StreamingDecoder::new(compressed.as_slice()).unwrap();
let mut head = vec![0u8; 4096];
let got = decoder.read(&mut head).unwrap();
assert!(got > 0);
let mut out = Vec::new();
out.extend_from_slice(&head[..got]);
let result = decoder.read_to_end(&mut out);
assert!(
result.is_err(),
"truncated current frame must fail the decode"
);
assert!(
out.len() <= payload.len(),
"failed fallback read must not leave a zero-filled tail (len {} > payload {})",
out.len(),
payload.len()
);
assert_eq!(
out.as_slice(),
&payload[..out.len()],
"decoded prefix must match the payload, with no appended non-decoded bytes"
);
}
#[cfg(feature = "std")]
#[test]
fn read_to_end_empty_frame_decodes_to_empty() {
use crate::encoding::{CompressionLevel, compress_slice_to_vec};
use alloc::vec::Vec;
let compressed = compress_slice_to_vec(&[], CompressionLevel::Level(3));
let mut decoder = StreamingDecoder::new(compressed.as_slice()).unwrap();
let mut out = Vec::new();
decoder.read_to_end(&mut out).unwrap();
assert!(out.is_empty());
}