Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1,327 @@
macro_rules! io_algo {
($impl:ident, $algo:ident($encoder:ident, $decoder:ident)) => {
pub mod $impl {
pub mod read {
pub use crate::utils::impls::$impl::read::{poll_read, to_vec};
}
pub mod bufread {
pub use crate::utils::impls::$impl::bufread::{from, AsyncBufRead};
pub use async_compression::$impl::bufread::{
$decoder as Decoder, $encoder as Encoder,
};
use crate::utils::{pin_mut, Level};
pub fn compress(input: impl AsyncBufRead) -> Vec<u8> {
pin_mut!(input);
super::read::to_vec(Encoder::with_quality(input, Level::Fastest))
}
pub fn decompress(input: impl AsyncBufRead) -> Vec<u8> {
pin_mut!(input);
super::read::to_vec(Decoder::new(input))
}
}
pub mod write {
pub use crate::utils::impls::$impl::write::to_vec;
pub use async_compression::$impl::write::{
$decoder as Decoder, $encoder as Encoder,
};
use crate::utils::Level;
pub fn compress(input: &[Vec<u8>], limit: usize) -> Vec<u8> {
to_vec(
input,
|input| Box::pin(Encoder::with_quality(input, Level::Fastest)),
limit,
)
}
pub fn decompress(input: &[Vec<u8>], limit: usize) -> Vec<u8> {
to_vec(input, |input| Box::pin(Decoder::new(input)), limit)
}
}
}
};
}
macro_rules! algos {
($(pub mod $name:ident($feat:literal, $encoder:ident, $decoder:ident) { pub mod sync { $($tt:tt)* } })*) => {
$(
#[cfg(feature = $feat)]
pub mod $name {
pub mod sync { $($tt)* }
#[cfg(feature = "futures-io")]
io_algo!(futures, $name($encoder, $decoder));
#[cfg(feature = "tokio")]
io_algo!(tokio, $name($encoder, $decoder));
}
)*
}
}
algos! {
pub mod brotli("brotli", BrotliEncoder, BrotliDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use brotli::{enc::backward_references::BrotliEncoderParams, CompressorReader};
let params = BrotliEncoderParams { quality: 1, ..Default::default() };
to_vec(CompressorReader::with_params(bytes, 0, &params))
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use brotli::Decompressor;
to_vec(Decompressor::new(bytes, 0))
}
}
}
pub mod bzip2("bzip2", BzEncoder, BzDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use bzip2::{bufread::BzEncoder, Compression};
to_vec(BzEncoder::new(bytes, Compression::fast()))
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use bzip2::bufread::BzDecoder;
to_vec(BzDecoder::new(bytes))
}
}
}
pub mod deflate("deflate", DeflateEncoder, DeflateDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use flate2::{bufread::DeflateEncoder, Compression};
to_vec(DeflateEncoder::new(bytes, Compression::fast()))
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use flate2::bufread::DeflateDecoder;
to_vec(DeflateDecoder::new(bytes))
}
}
}
pub mod zlib("zlib", ZlibEncoder, ZlibDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use flate2::{bufread::ZlibEncoder, Compression};
to_vec(ZlibEncoder::new(bytes, Compression::fast()))
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use flate2::bufread::ZlibDecoder;
to_vec(ZlibDecoder::new(bytes))
}
}
}
pub mod gzip("gzip", GzipEncoder, GzipDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use flate2::{bufread::GzEncoder, Compression};
to_vec(GzEncoder::new(bytes, Compression::fast()))
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use flate2::bufread::GzDecoder;
to_vec(GzDecoder::new(bytes))
}
}
}
pub mod zstd("zstd", ZstdEncoder, ZstdDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use libzstd::stream::read::Encoder;
use libzstd::DEFAULT_COMPRESSION_LEVEL;
to_vec(Encoder::new(bytes, DEFAULT_COMPRESSION_LEVEL).unwrap())
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use libzstd::stream::read::Decoder;
to_vec(Decoder::new(bytes).unwrap())
}
}
}
pub mod xz("xz", XzEncoder, XzDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use liblzma::bufread::XzEncoder;
to_vec(XzEncoder::new(bytes, 0))
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use liblzma::bufread::XzDecoder;
to_vec(XzDecoder::new(bytes))
}
}
}
pub mod lzma("lzma", LzmaEncoder, LzmaDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use liblzma::bufread::XzEncoder;
use liblzma::stream::{LzmaOptions, Stream};
to_vec(XzEncoder::new_stream(
bytes,
Stream::new_lzma_encoder(&LzmaOptions::new_preset(0).unwrap()).unwrap(),
))
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use liblzma::bufread::XzDecoder;
use liblzma::stream::Stream;
to_vec(XzDecoder::new_stream(
bytes,
Stream::new_lzma_decoder(u64::MAX).unwrap(),
))
}
}
}
pub mod lz4("lz4", Lz4Encoder, Lz4Decoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use std::io::Write;
use lz4::EncoderBuilder;
let mut encoder = EncoderBuilder::new().build(vec![]).unwrap();
encoder.write_all(bytes).unwrap();
let (compressed_bytes, result) = encoder.finish();
result.unwrap();
compressed_bytes
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use lz4::Decoder;
to_vec(Decoder::new(bytes).unwrap())
}
}
}
}
macro_rules! io_algo_parallel {
($impl:ident, $algo:ident($encoder:ident, $decoder:ident)) => {
pub mod $impl {
const THREADS: std::num::NonZeroU32 = std::num::NonZeroU32::new(16).unwrap();
pub mod read {
pub use crate::utils::impls::$impl::read::{poll_read, to_vec};
}
pub mod bufread {
pub use crate::utils::impls::$impl::bufread::{from, AsyncBufRead};
pub use async_compression::$impl::bufread::{
$decoder as Decoder, $encoder as Encoder,
};
use super::THREADS;
use crate::utils::{pin_mut, Level};
pub fn compress(input: impl AsyncBufRead) -> Vec<u8> {
pin_mut!(input);
super::read::to_vec(Encoder::parallel(input, Level::Fastest, THREADS))
}
pub fn decompress(input: impl AsyncBufRead) -> Vec<u8> {
pin_mut!(input);
super::read::to_vec(Decoder::parallel(input, THREADS))
}
}
pub mod write {
pub use crate::utils::impls::$impl::write::to_vec;
pub use async_compression::$impl::write::{
$decoder as Decoder, $encoder as Encoder,
};
use super::THREADS;
use crate::utils::Level;
pub fn compress(input: &[Vec<u8>], limit: usize) -> Vec<u8> {
to_vec(
input,
|input| Box::pin(Encoder::parallel(input, Level::Fastest, THREADS)),
limit,
)
}
pub fn decompress(input: &[Vec<u8>], limit: usize) -> Vec<u8> {
to_vec(
input,
|input| Box::pin(Decoder::parallel(input, THREADS)),
limit,
)
}
}
}
};
}
macro_rules! algos_parallel {
($(pub mod $name:ident($feat:literal, $encoder:ident, $decoder:ident) { pub mod sync { $($tt:tt)* } })*) => {
$(
#[cfg(feature = $feat)]
pub mod $name {
pub mod sync { $($tt)* }
#[cfg(feature = "futures-io")]
io_algo_parallel!(futures, $name($encoder, $decoder));
#[cfg(feature = "tokio")]
io_algo_parallel!(tokio, $name($encoder, $decoder));
}
)*
}
}
algos_parallel! {
pub mod xz_parallel("xz-parallel", XzEncoder, XzDecoder) {
pub mod sync {
pub use crate::utils::impls::sync::to_vec;
pub fn compress(bytes: &[u8]) -> Vec<u8> {
use liblzma::bufread::XzEncoder;
to_vec(XzEncoder::new(bytes, 0))
}
pub fn decompress(bytes: &[u8]) -> Vec<u8> {
use liblzma::bufread::XzDecoder;
to_vec(XzDecoder::new(bytes))
}
}
}
}
@@ -0,0 +1,155 @@
pub mod sync {
use std::io::Read;
pub fn to_vec(mut read: impl Read) -> Vec<u8> {
let mut output = vec![];
read.read_to_end(&mut output).unwrap();
output
}
}
#[cfg(feature = "futures-io")]
pub mod futures {
pub mod bufread {
pub use futures::io::AsyncBufRead;
use crate::utils::{InputStream, TrackEof};
use futures::stream::{StreamExt as _, TryStreamExt as _};
pub fn from(input: &InputStream) -> impl AsyncBufRead {
// By using the stream here we ensure that each chunk will require a separate
// read/poll_fill_buf call to process to help test reading multiple chunks.
TrackEof::new(input.stream().map(Ok).into_async_read())
}
}
pub mod read {
use crate::utils::{block_on, pin_mut};
use futures::io::{copy_buf, AsyncRead, AsyncReadExt, BufReader, Cursor};
pub fn to_vec(read: impl AsyncRead) -> Vec<u8> {
// TODO: https://github.com/rust-lang-nursery/futures-rs/issues/1510
// All current test cases are < 100kB
let mut output = Cursor::new(vec![0; 102_400]);
pin_mut!(read);
// With more flushing from encoders, 4 appears to be the minimal buffer size that works.
let len = block_on(copy_buf(BufReader::with_capacity(4, read), &mut output)).unwrap();
let mut output = output.into_inner();
output.truncate(len as usize);
output
}
pub fn poll_read(reader: impl AsyncRead, output: &mut [u8]) -> std::io::Result<usize> {
pin_mut!(reader);
block_on(reader.read(output))
}
}
pub mod write {
use crate::utils::{block_on, Pin, TrackClosed};
use futures::io::{AsyncWrite, AsyncWriteExt as _};
use futures_test::io::AsyncWriteTestExt as _;
pub fn to_vec(
input: &[Vec<u8>],
create_writer: impl for<'a> FnOnce(
&'a mut (dyn AsyncWrite + Unpin),
) -> Pin<Box<dyn AsyncWrite + 'a>>,
limit: usize,
) -> Vec<u8> {
let mut output = Vec::new();
{
let mut test_writer = TrackClosed::new(
(&mut output)
.limited_write(limit)
.interleave_pending_write(),
);
{
let mut writer = create_writer(&mut test_writer);
for chunk in input {
block_on(writer.write_all(chunk)).unwrap();
block_on(writer.flush()).unwrap();
}
block_on(writer.close()).unwrap();
}
assert!(test_writer.is_closed());
}
output
}
}
}
#[cfg(feature = "tokio")]
pub mod tokio {
pub mod bufread {
use crate::utils::{InputStream, TrackEof};
use bytes::Bytes;
use futures::stream::StreamExt;
pub use tokio::io::AsyncBufRead;
use tokio_util::io::StreamReader;
pub fn from(input: &InputStream) -> impl AsyncBufRead {
// By using the stream here we ensure that each chunk will require a separate
// read/poll_fill_buf call to process to help test reading multiple chunks.
TrackEof::new(StreamReader::new(
input.stream().map(Bytes::from).map(std::io::Result::Ok),
))
}
}
pub mod read {
use crate::utils::{block_on, pin_mut, tokio_ext::copy_buf};
use std::io::Cursor;
use tokio::io::{AsyncRead, AsyncReadExt, BufReader};
pub fn to_vec(read: impl AsyncRead) -> Vec<u8> {
let mut output = Cursor::new(vec![0; 102_400]);
pin_mut!(read);
// With more flushing from encoders, 4 appears to be the minimal buffer size that works.
let len = block_on(copy_buf(BufReader::with_capacity(4, read), &mut output)).unwrap();
let mut output = output.into_inner();
output.truncate(len as usize);
output
}
pub fn poll_read(reader: impl AsyncRead, output: &mut [u8]) -> std::io::Result<usize> {
pin_mut!(reader);
block_on(reader.read(output))
}
}
pub mod write {
use crate::utils::{
block_on, tokio_ext::AsyncWriteTestExt as _, track_closed::TrackClosed, Pin,
};
use std::io::Cursor;
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
pub fn to_vec(
input: &[Vec<u8>],
create_writer: impl for<'a> FnOnce(
&'a mut (dyn AsyncWrite + Unpin),
) -> Pin<Box<dyn AsyncWrite + 'a>>,
limit: usize,
) -> Vec<u8> {
let mut output = Cursor::new(Vec::new());
{
let mut test_writer = TrackClosed::new(
(&mut output)
.limited_write(limit)
.interleave_pending_write(),
);
{
let mut writer = create_writer(&mut test_writer);
for chunk in input {
block_on(writer.write_all(chunk)).unwrap();
block_on(writer.flush()).unwrap();
}
block_on(writer.shutdown()).unwrap();
}
assert!(test_writer.is_closed());
}
output.into_inner()
}
}
}
@@ -0,0 +1,48 @@
use futures::stream::Stream;
use futures_test::stream::StreamTestExt as _;
use proptest_derive::Arbitrary;
#[derive(Arbitrary, Debug, Clone)]
pub struct InputStream(Vec<Vec<u8>>);
impl InputStream {
pub fn new(input: Vec<Vec<u8>>) -> Self {
InputStream(input)
}
pub fn as_ref(&self) -> &[Vec<u8>] {
&self.0
}
pub fn stream(&self) -> impl Stream<Item = Vec<u8>> {
// The resulting stream here will interleave empty chunks before and after each chunk, and
// then interleave a `Poll::Pending` between each yielded chunk, that way we test the
// handling of these two conditions in every point of the tested stream.
futures::stream::iter(
self.0
.clone()
.into_iter()
.flat_map(|bytes| vec![vec![], bytes])
.chain(Some(vec![])),
)
.interleave_pending()
}
pub fn bytes(&self) -> Vec<u8> {
self.0.iter().flatten().cloned().collect()
}
pub fn len(&self) -> usize {
self.0.iter().map(Vec::len).sum()
}
}
impl<I> From<I> for InputStream
where
I: IntoIterator,
I::Item: Into<Vec<u8>>,
{
fn from(input: I) -> InputStream {
Self::new(input.into_iter().map(|b| b.into()).collect())
}
}
@@ -0,0 +1,25 @@
#![allow(dead_code, unused_imports, unused_macros)] // Different tests use a different subset of functions
mod input_stream;
#[cfg(feature = "tokio")]
mod tokio_ext;
mod track_closed;
mod track_eof;
#[macro_use]
mod test_cases;
pub mod algos;
pub mod impls;
pub use self::{input_stream::InputStream, track_closed::TrackClosed, track_eof::TrackEof};
pub use compression_core::Level;
pub use futures::{executor::block_on, pin_mut, stream::Stream};
pub use std::{future::Future, io::Result, iter::FromIterator, pin::Pin};
pub fn one_to_six_stream() -> InputStream {
InputStream::new(vec![vec![1, 2, 3], vec![4, 5, 6]])
}
pub fn one_to_six() -> &'static [u8] {
&[1, 2, 3, 4, 5, 6]
}
@@ -0,0 +1,477 @@
macro_rules! io_test_cases {
($impl:ident, $variant:ident) => {
mod $impl {
mod bufread {
mod compress {
use crate::utils::{
algos::$variant::{
sync,
$impl::{bufread, read},
},
one_to_six, one_to_six_stream, InputStream, Level,
};
#[test]
#[ntest::timeout(1000)]
fn empty() {
let mut input: &[u8] = &[];
let compressed = bufread::compress(&mut input);
let output = sync::decompress(&compressed);
assert_eq!(output, &[][..]);
}
#[test]
#[ntest::timeout(1000)]
fn to_full_output() {
let mut output = [];
let encoder = bufread::Encoder::new(bufread::from(&one_to_six_stream()));
let result = read::poll_read(encoder, &mut output);
assert!(matches!(result, Ok(0)));
}
#[test]
#[ntest::timeout(1000)]
fn empty_chunk() {
let input = InputStream::new(vec![vec![]]);
let compressed = bufread::compress(bufread::from(&input));
let output = sync::decompress(&compressed);
assert_eq!(output, input.bytes());
}
#[test]
#[ntest::timeout(1000)]
fn short() {
let compressed = bufread::compress(bufread::from(&one_to_six_stream()));
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
#[ntest::timeout(1000)]
fn long() {
let input = InputStream::new(vec![
(0..32_768).map(|_| rand::random()).collect(),
(0..32_768).map(|_| rand::random()).collect(),
]);
let compressed = bufread::compress(bufread::from(&input));
let output = sync::decompress(&compressed);
assert_eq!(output, input.bytes());
}
#[test]
fn with_level_best() {
let encoder = bufread::Encoder::with_quality(
bufread::from(&one_to_six_stream()),
Level::Best,
);
let compressed = read::to_vec(encoder);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
fn with_level_default() {
let encoder = bufread::Encoder::new(bufread::from(&one_to_six_stream()));
let compressed = read::to_vec(encoder);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
fn with_level_0() {
let encoder = bufread::Encoder::with_quality(
bufread::from(&one_to_six_stream()),
Level::Precise(0),
);
let compressed = read::to_vec(encoder);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
fn with_level_max() {
let encoder = bufread::Encoder::with_quality(
bufread::from(&one_to_six_stream()),
Level::Precise(i32::MAX),
);
let compressed = read::to_vec(encoder);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
}
mod decompress {
use crate::utils::{
algos::$variant::{
sync,
$impl::{bufread, read},
},
one_to_six, one_to_six_stream, InputStream,
};
#[test]
#[ntest::timeout(1000)]
fn empty() {
let compressed = sync::compress(&[]);
let input = InputStream::new(vec![compressed]);
let output = bufread::decompress(bufread::from(&input));
assert_eq!(output, &[][..]);
}
#[test]
#[ntest::timeout(1000)]
fn to_full_output() {
let mut output = [];
let decoder = bufread::Decoder::new(bufread::from(&one_to_six_stream()));
let result = read::poll_read(decoder, &mut output);
assert!(matches!(result, Ok(0)));
}
#[test]
#[ntest::timeout(1000)]
fn zeros() {
let compressed = sync::compress(&[0; 10]);
let input = InputStream::new(vec![compressed]);
let output = bufread::decompress(bufread::from(&input));
assert_eq!(output, &[0; 10][..]);
}
#[test]
#[ntest::timeout(1000)]
fn short() {
let compressed = sync::compress(&[1, 2, 3, 4, 5, 6]);
let input = InputStream::new(vec![compressed]);
let output = bufread::decompress(bufread::from(&input));
assert_eq!(output, one_to_six());
}
#[test]
#[ntest::timeout(1000)]
fn short_chunks() {
let compressed = sync::compress(&[1, 2, 3, 4, 5, 6]);
let input = InputStream::from(compressed.chunks(2));
let output = bufread::decompress(bufread::from(&input));
assert_eq!(output, one_to_six());
}
#[test]
#[ntest::timeout(1000)]
fn trailer() {
let mut compressed = sync::compress(&[1, 2, 3, 4, 5, 6]);
compressed.extend_from_slice(&[7, 8, 9, 10]);
let input = InputStream::new(vec![compressed]);
let mut reader = bufread::from(&input);
let output = bufread::decompress(&mut reader);
let trailer = read::to_vec(reader);
assert_eq!(output, one_to_six());
assert_eq!(trailer, &[7, 8, 9, 10][..]);
}
#[test]
#[ntest::timeout(1000)]
fn long() {
let bytes: Vec<u8> = (0..65_536).map(|_| rand::random()).collect();
let compressed = sync::compress(&bytes);
let input = InputStream::new(vec![compressed]);
let output = bufread::decompress(bufread::from(&input));
assert_eq!(output, bytes);
}
#[test]
#[ntest::timeout(1000)]
fn long_chunks() {
let bytes: Vec<u8> = (0..65_536).map(|_| rand::random()).collect();
let compressed = sync::compress(&bytes);
let input = InputStream::from(compressed.chunks(1024));
let output = bufread::decompress(bufread::from(&input));
assert_eq!(output, bytes);
}
#[test]
#[ntest::timeout(1000)]
fn multiple_members() {
let compressed = [
sync::compress(&[1, 2, 3, 4, 5, 6]),
sync::compress(&[6, 5, 4, 3, 2, 1]),
]
.join(&[][..]);
let input = InputStream::new(vec![compressed]);
let mut decoder = bufread::Decoder::new(bufread::from(&input));
decoder.multiple_members(true);
let output = read::to_vec(decoder);
assert_eq!(output, &[1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1][..]);
}
#[test]
#[ntest::timeout(1000)]
fn truncated() {
let compressed = sync::compress(&[1, 2, 3, 4, 5, 6]);
// Truncate the compressed data (remove last 20 bytes or half, whichever is less)
let truncate_amount = std::cmp::min(20, compressed.len() / 2);
let truncated = &compressed[..compressed.len() - truncate_amount];
let input = InputStream::new(vec![truncated.to_vec()]);
// Try to decompress - should get an error for incomplete stream
// The error manifests as a panic when read::to_vec calls unwrap()
let result =
std::panic::catch_unwind(|| bufread::decompress(bufread::from(&input)));
// Should fail for truncated stream
assert!(result.is_err(), "Expected error for truncated stream");
}
}
}
mod write {
mod compress {
use crate::utils::{
algos::$variant::{sync, $impl::write},
one_to_six, one_to_six_stream, InputStream, Level,
};
#[test]
#[ntest::timeout(1000)]
fn empty() {
let input = InputStream::new(vec![]);
let compressed = write::compress(input.as_ref(), 65_536);
let output = sync::decompress(&compressed);
assert_eq!(output, &[][..]);
}
#[test]
#[ntest::timeout(1000)]
fn empty_chunk() {
let input = InputStream::new(vec![vec![]]);
let compressed = write::compress(input.as_ref(), 65_536);
let output = sync::decompress(&compressed);
assert_eq!(output, input.bytes());
}
#[test]
#[ntest::timeout(1000)]
fn short() {
let compressed = write::compress(one_to_six_stream().as_ref(), 65_536);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
#[ntest::timeout(1000)]
fn short_chunk_output() {
let compressed = write::compress(one_to_six_stream().as_ref(), 2);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
#[ntest::timeout(1000)]
fn long() {
let input = InputStream::new(vec![
(0..32_768).map(|_| rand::random()).collect(),
(0..32_768).map(|_| rand::random()).collect(),
]);
let compressed = write::compress(input.as_ref(), 65_536);
let output = sync::decompress(&compressed);
assert_eq!(output, input.bytes());
}
#[test]
#[ntest::timeout(1000)]
fn long_chunk_output() {
let input = InputStream::new(vec![
(0..32_768).map(|_| rand::random()).collect(),
(0..32_768).map(|_| rand::random()).collect(),
]);
let compressed = write::compress(input.as_ref(), 20);
let output = sync::decompress(&compressed);
assert_eq!(output, input.bytes());
}
#[test]
fn with_level_best() {
let compressed = write::to_vec(
one_to_six_stream().as_ref(),
|input| Box::pin(write::Encoder::with_quality(input, Level::Best)),
65_536,
);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
fn with_level_default() {
let compressed = write::to_vec(
one_to_six_stream().as_ref(),
|input| Box::pin(write::Encoder::new(input)),
65_536,
);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
fn with_level_0() {
let compressed = write::to_vec(
one_to_six_stream().as_ref(),
|input| {
Box::pin(write::Encoder::with_quality(input, Level::Precise(0)))
},
65_536,
);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
#[test]
fn with_level_max() {
let compressed = write::to_vec(
one_to_six_stream().as_ref(),
|input| {
Box::pin(write::Encoder::with_quality(
input,
Level::Precise(i32::MAX),
))
},
65_536,
);
let output = sync::decompress(&compressed);
assert_eq!(output, one_to_six());
}
}
mod decompress {
use crate::utils::{
algos::$variant::{sync, $impl::write},
one_to_six, InputStream,
};
#[test]
#[ntest::timeout(1000)]
fn empty() {
let compressed = sync::compress(&[]);
let input = InputStream::new(vec![compressed]);
let output = write::decompress(input.as_ref(), 65_536);
assert_eq!(output, &[][..]);
}
#[test]
#[ntest::timeout(1000)]
fn zeros() {
let compressed = sync::compress(&[0; 10]);
let input = InputStream::new(vec![compressed]);
let output = write::decompress(input.as_ref(), 65_536);
assert_eq!(output, &[0; 10][..]);
}
#[test]
#[ntest::timeout(1000)]
fn short() {
let compressed = sync::compress(&[1, 2, 3, 4, 5, 6]);
let input = InputStream::new(vec![compressed]);
let output = write::decompress(input.as_ref(), 65_536);
assert_eq!(output, one_to_six());
}
#[test]
#[ntest::timeout(1000)]
fn short_chunks() {
let compressed = sync::compress(&[1, 2, 3, 4, 5, 6]);
let input = InputStream::from(compressed.chunks(2));
let output = write::decompress(input.as_ref(), 65_536);
assert_eq!(output, one_to_six());
}
#[test]
#[ntest::timeout(1000)]
fn long() {
let bytes: Vec<u8> = (0..65_536).map(|_| rand::random()).collect();
let compressed = sync::compress(&bytes);
let input = InputStream::new(vec![compressed]);
let output = write::decompress(input.as_ref(), 65_536);
assert_eq!(output, bytes);
}
#[test]
#[ntest::timeout(1000)]
fn long_chunks() {
let bytes: Vec<u8> = (0..65_536).map(|_| rand::random()).collect();
let compressed = sync::compress(&bytes);
let input = InputStream::from(compressed.chunks(1024));
let output = write::decompress(input.as_ref(), 65_536);
assert_eq!(output, bytes);
}
}
}
}
};
}
macro_rules! test_cases {
($variant:ident) => {
mod $variant {
#[cfg(feature = "futures-io")]
io_test_cases!(futures, $variant);
#[cfg(feature = "tokio")]
io_test_cases!(tokio, $variant);
}
};
}
@@ -0,0 +1,52 @@
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use futures::ready;
use tokio::io::{AsyncBufRead, AsyncWrite};
pub fn copy_buf<R, W>(reader: R, writer: &mut W) -> CopyBuf<'_, R, W>
where
R: AsyncBufRead + Unpin,
W: AsyncWrite + Unpin + ?Sized,
{
CopyBuf {
reader,
writer,
amt: 0,
}
}
#[derive(Debug)]
pub struct CopyBuf<'a, R, W: ?Sized> {
reader: R,
writer: &'a mut W,
amt: u64,
}
impl<R, W> Future for CopyBuf<'_, R, W>
where
R: AsyncBufRead + Unpin,
W: AsyncWrite + Unpin + ?Sized,
{
type Output = std::io::Result<u64>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = &mut *self;
loop {
let buffer = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
if buffer.is_empty() {
ready!(Pin::new(&mut this.writer).poll_flush(cx))?;
return Poll::Ready(Ok(this.amt));
}
let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?;
if i == 0 {
return Poll::Ready(Err(std::io::ErrorKind::WriteZero.into()));
}
this.amt += i as u64;
Pin::new(&mut this.reader).consume(i);
}
}
}
@@ -0,0 +1,66 @@
use std::{
pin::Pin,
task::{Context, Poll},
};
pub struct InterleavePending<T> {
inner: T,
pended: bool,
}
impl<T> InterleavePending<T> {
pub(crate) fn new(inner: T) -> Self {
Self {
inner,
pended: false,
}
}
}
impl<W: tokio::io::AsyncWrite + Unpin> tokio::io::AsyncWrite for InterleavePending<W> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
if self.pended {
let next = Pin::new(&mut self.inner).poll_write(cx, buf);
if next.is_ready() {
self.pended = false;
}
next
} else {
cx.waker().wake_by_ref();
self.pended = true;
Poll::Pending
}
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
if self.pended {
let next = Pin::new(&mut self.inner).poll_flush(cx);
if next.is_ready() {
self.pended = false;
}
next
} else {
cx.waker().wake_by_ref();
self.pended = true;
Poll::Pending
}
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
if self.pended {
let next = Pin::new(&mut self.inner).poll_shutdown(cx);
if next.is_ready() {
self.pended = false;
}
next
} else {
cx.waker().wake_by_ref();
self.pended = true;
Poll::Pending
}
}
}
@@ -0,0 +1,35 @@
use std::{
pin::Pin,
task::{Context, Poll},
};
#[derive(Debug)]
pub struct Limited<Io> {
io: Io,
limit: usize,
}
impl<Io> Limited<Io> {
pub(crate) fn new(io: Io, limit: usize) -> Limited<Io> {
Limited { io, limit }
}
}
impl<W: tokio::io::AsyncWrite + Unpin> tokio::io::AsyncWrite for Limited<W> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
let limit = self.limit;
Pin::new(&mut self.io).poll_write(cx, &buf[..std::cmp::min(limit, buf.len())])
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.io).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Pin::new(&mut self.io).poll_shutdown(cx)
}
}
@@ -0,0 +1,23 @@
mod copy_buf;
mod interleave_pending;
mod limited;
pub use copy_buf::copy_buf;
pub trait AsyncWriteTestExt: tokio::io::AsyncWrite {
fn interleave_pending_write(self) -> interleave_pending::InterleavePending<Self>
where
Self: Sized + Unpin,
{
interleave_pending::InterleavePending::new(self)
}
fn limited_write(self, limit: usize) -> limited::Limited<Self>
where
Self: Sized + Unpin,
{
limited::Limited::new(self, limit)
}
}
impl<T: tokio::io::AsyncWrite> AsyncWriteTestExt for T {}
@@ -0,0 +1,89 @@
#[cfg_attr(not(feature = "all-implementations"), allow(unused))]
use std::{
io::Result,
pin::Pin,
task::{Context, Poll},
};
pub struct TrackClosed<W> {
inner: W,
closed: bool,
}
impl<W> TrackClosed<W> {
pub fn new(inner: W) -> Self {
Self {
inner,
closed: false,
}
}
pub fn is_closed(&self) -> bool {
self.closed
}
}
#[cfg(feature = "futures-io")]
impl<W: futures::io::AsyncWrite + Unpin> futures::io::AsyncWrite for TrackClosed<W> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize>> {
assert!(!self.closed);
Pin::new(&mut self.inner).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
assert!(!self.closed);
Pin::new(&mut self.inner).poll_flush(cx)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
assert!(!self.closed);
match Pin::new(&mut self.inner).poll_close(cx) {
Poll::Ready(Ok(())) => {
self.closed = true;
Poll::Ready(Ok(()))
}
other => other,
}
}
fn poll_write_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[std::io::IoSlice<'_>],
) -> Poll<Result<usize>> {
assert!(!self.closed);
Pin::new(&mut self.inner).poll_write_vectored(cx, bufs)
}
}
#[cfg(feature = "tokio")]
impl<W: tokio::io::AsyncWrite + Unpin> tokio::io::AsyncWrite for TrackClosed<W> {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize>> {
assert!(!self.closed);
Pin::new(&mut self.inner).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
assert!(!self.closed);
Pin::new(&mut self.inner).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
assert!(!self.closed);
match Pin::new(&mut self.inner).poll_shutdown(cx) {
Poll::Ready(Ok(())) => {
self.closed = true;
Poll::Ready(Ok(()))
}
other => other,
}
}
}
@@ -0,0 +1,107 @@
#[cfg_attr(not(feature = "all-implementations"), allow(unused))]
use std::{
io::Result,
pin::Pin,
task::{Context, Poll},
};
pub struct TrackEof<R> {
inner: R,
eof: bool,
}
impl<R: Unpin> TrackEof<R> {
pub fn new(inner: R) -> Self {
Self { inner, eof: false }
}
pub fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, &mut bool) {
let Self { inner, eof } = Pin::into_inner(self);
(Pin::new(inner), eof)
}
}
#[cfg(feature = "futures-io")]
impl<R: futures::io::AsyncRead + Unpin> futures::io::AsyncRead for TrackEof<R> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize>> {
let (inner, eof) = self.project();
assert!(!*eof);
match inner.poll_read(cx, buf) {
Poll::Ready(Ok(0)) => {
if !buf.is_empty() {
*eof = true;
}
Poll::Ready(Ok(0))
}
other => other,
}
}
}
#[cfg(feature = "futures-io")]
impl<R: futures::io::AsyncBufRead + Unpin> futures::io::AsyncBufRead for TrackEof<R> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
let (inner, eof) = self.project();
assert!(!*eof);
match inner.poll_fill_buf(cx) {
Poll::Ready(Ok(buf)) => {
if buf.is_empty() {
*eof = true;
}
Poll::Ready(Ok(buf))
}
other => other,
}
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.project().0.consume(amt)
}
}
#[cfg(feature = "tokio")]
impl<R: tokio::io::AsyncRead + Unpin> tokio::io::AsyncRead for TrackEof<R> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<Result<()>> {
let (inner, eof) = self.project();
assert!(!*eof);
let len = buf.filled().len();
match inner.poll_read(cx, buf) {
Poll::Ready(Ok(())) => {
if buf.filled().len() == len && buf.remaining() > 0 {
*eof = true;
}
Poll::Ready(Ok(()))
}
other => other,
}
}
}
#[cfg(feature = "tokio")]
impl<R: tokio::io::AsyncBufRead + Unpin> tokio::io::AsyncBufRead for TrackEof<R> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
let (inner, eof) = self.project();
assert!(!*eof);
match inner.poll_fill_buf(cx) {
Poll::Ready(Ok(buf)) => {
if buf.is_empty() {
*eof = true;
}
Poll::Ready(Ok(buf))
}
other => other,
}
}
fn consume(self: Pin<&mut Self>, amt: usize) {
self.project().0.consume(amt)
}
}