extern crate std;
use crate as zstd_safe;
use self::std::vec::Vec;
const INPUT: &[u8] = b"Rust is a multi-paradigm system programming language focused on safety, especially safe concurrency. Rust is syntactically similar to C++, but is designed to provide better memory safety while maintaining high performance.";
const LONG_CONTENT: &str = include_str!("lib.rs");
#[cfg(feature = "std")]
#[test]
fn test_writebuf() {
use zstd_safe::WriteBuf;
let mut data = Vec::with_capacity(10);
unsafe {
data.write_from(|ptr, n| {
assert!(n >= 4);
let ptr = ptr as *mut u8;
ptr.write(0);
ptr.add(1).write(1);
ptr.add(2).write(2);
ptr.add(3).write(3);
Ok(4)
})
}
.unwrap();
assert_eq!(data.as_slice(), &[0, 1, 2, 3]);
let mut cursor = std::io::Cursor::new(&mut data);
cursor.set_position(6);
unsafe {
cursor.write_from(|ptr, n| {
assert!(n >= 4);
let ptr = ptr as *mut u8;
ptr.write(4);
ptr.add(1).write(5);
ptr.add(2).write(6);
ptr.add(3).write(7);
Ok(4)
})
}
.unwrap();
assert_eq!(data.as_slice(), &[0, 1, 2, 3, 0, 0, 4, 5, 6, 7]);
}
#[cfg(feature = "std")]
#[test]
fn test_simple_cycle() {
let mut buffer = std::vec![0u8; 256];
let written = zstd_safe::compress(&mut buffer, INPUT, 3).unwrap();
let compressed = &buffer[..written];
let mut buffer = std::vec![0u8; 256];
let written = zstd_safe::decompress(&mut buffer, compressed).unwrap();
let decompressed = &buffer[..written];
assert_eq!(INPUT, decompressed);
}
#[test]
fn test_cctx_cycle() {
let mut buffer = std::vec![0u8; 256];
let mut cctx = zstd_safe::CCtx::default();
let written = cctx.compress(&mut buffer[..], INPUT, 1).unwrap();
let compressed = &buffer[..written];
let mut dctx = zstd_safe::DCtx::default();
let mut buffer = std::vec![0u8; 256];
let written = dctx.decompress(&mut buffer[..], compressed).unwrap();
let decompressed = &buffer[..written];
assert_eq!(INPUT, decompressed);
}
#[test]
fn test_dictionary() {
let bytes = LONG_CONTENT.as_bytes();
let line_sizes: Vec<usize> = LONG_CONTENT
.split_inclusive('\n')
.map(|line| line.len())
.collect();
let mut dict_buffer = std::vec![0u8; 100_000];
let written =
zstd_safe::train_from_buffer(&mut dict_buffer[..], bytes, &line_sizes)
.unwrap();
let dict_buffer = &dict_buffer[..written];
let cdict = zstd_safe::create_cdict(dict_buffer, 3);
let ddict = zstd_safe::create_ddict(dict_buffer);
let mut cctx = zstd_safe::CCtx::default();
cctx.ref_cdict(&cdict).unwrap();
let mut buffer = std::vec![0u8; 1024 * 1024];
let big_written = zstd_safe::compress(&mut buffer[..], bytes, 3).unwrap();
let written = cctx
.compress2(&mut buffer[..], bytes)
.map_err(zstd_safe::get_error_name)
.unwrap();
assert!(big_written > written);
let compressed = &buffer[..written];
let mut dctx = zstd_safe::DCtx::default();
dctx.ref_ddict(&ddict).unwrap();
let mut buffer = std::vec![0u8; 1024 * 1024];
let written = dctx
.decompress(&mut buffer[..], compressed)
.map_err(zstd_safe::get_error_name)
.unwrap();
let decompressed = &buffer[..written];
assert_eq!(bytes, decompressed);
}
#[test]
fn test_checksum() {
let mut buffer = std::vec![0u8; 256];
let mut cctx = zstd_safe::CCtx::default();
cctx.set_parameter(zstd_safe::CParameter::ChecksumFlag(true))
.unwrap();
let written = cctx.compress2(&mut buffer[..], INPUT).unwrap();
let compressed = &mut buffer[..written];
let mut dctx = zstd_safe::DCtx::default();
let mut buffer = std::vec![0u8; 1024*1024];
let written = dctx
.decompress(&mut buffer[..], compressed)
.map_err(zstd_safe::get_error_name)
.unwrap();
let decompressed = &buffer[..written];
assert_eq!(INPUT, decompressed);
if let Some(last) = compressed.last_mut() {
*last = last.saturating_sub(1);
}
let err = dctx
.decompress(&mut buffer[..], compressed)
.map_err(zstd_safe::get_error_name)
.err()
.unwrap();
assert!(err.contains("checksum"));
}
#[cfg(all(feature = "experimental", feature = "std"))]
#[test]
fn test_upper_bound() {
let mut buffer = std::vec![0u8; 256];
assert!(zstd_safe::decompress_bound(&buffer).is_err());
let written = zstd_safe::compress(&mut buffer, INPUT, 3).unwrap();
let compressed = &buffer[..written];
assert_eq!(
zstd_safe::decompress_bound(compressed),
Ok(INPUT.len() as u64)
);
}
#[cfg(feature = "seekable")]
#[test]
fn test_seekable_cycle() {
let seekable_archive = new_seekable_archive(INPUT);
let mut seekable = crate::seekable::Seekable::create();
seekable
.init_buff(&seekable_archive)
.map_err(zstd_safe::get_error_name)
.unwrap();
check_seekable_decompression(|dst, offset| {
seekable.decompress(dst, offset)
});
let mut buffer = std::vec![0u8; 256];
let written = zstd_safe::decompress(&mut buffer[..], &seekable_archive)
.map_err(zstd_safe::get_error_name)
.unwrap();
let decompressed = &buffer[..written];
assert_eq!(INPUT, decompressed);
let frame_index = seekable.num_frames() + 1;
assert_eq!(
seekable.frame_compressed_offset(frame_index).unwrap_err(),
crate::seekable::FrameIndexTooLargeError
);
}
#[cfg(feature = "seekable")]
#[test]
fn test_seekable_seek_table() {
use crate::seekable::{FrameIndexTooLargeError, SeekTable, Seekable};
let seekable_archive = new_seekable_archive(INPUT);
let mut seekable = Seekable::create();
assert!(SeekTable::try_from_seekable(&seekable).is_err());
seekable
.init_buff(&seekable_archive)
.map_err(zstd_safe::get_error_name)
.unwrap();
let seek_table = { SeekTable::try_from_seekable(&seekable).unwrap() };
assert_eq!(seekable.num_frames(), seek_table.num_frames());
assert_eq!(
seekable.frame_compressed_offset(2).unwrap(),
seek_table.frame_compressed_offset(2).unwrap()
);
assert_eq!(
seekable.frame_decompressed_offset(2).unwrap(),
seek_table.frame_decompressed_offset(2).unwrap()
);
assert_eq!(
seekable.frame_compressed_size(2).unwrap(),
seek_table.frame_compressed_size(2).unwrap()
);
assert_eq!(
seekable.frame_decompressed_size(2).unwrap(),
seek_table.frame_decompressed_size(2).unwrap()
);
let frame_index = seekable.num_frames() + 1;
assert_eq!(
seek_table.frame_compressed_offset(frame_index).unwrap_err(),
FrameIndexTooLargeError
);
}
#[cfg(all(feature = "std", feature = "seekable"))]
#[test]
fn test_seekable_advanced_cycle() {
use crate::seekable::Seekable;
use std::{boxed::Box, io::Cursor};
let seekable_archive = Cursor::new(new_seekable_archive(INPUT));
let mut seekable = Seekable::create()
.init_advanced(Box::new(seekable_archive))
.map_err(zstd_safe::get_error_name)
.unwrap();
check_seekable_decompression(|dst, offset| {
seekable.decompress(dst, offset)
});
}
#[cfg(feature = "seekable")]
fn new_seekable_archive(input: &[u8]) -> Vec<u8> {
use crate::{seekable::SeekableCStream, InBuffer, OutBuffer};
let mut buffer = std::vec![0u8; 512];
let mut cstream = SeekableCStream::create();
cstream
.init(3, true, 64)
.map_err(zstd_safe::get_error_name)
.unwrap();
let mut in_buffer = InBuffer::around(input);
let mut out_buffer = OutBuffer::around(&mut buffer[..]);
while in_buffer.pos() < in_buffer.src.len() {
cstream
.compress_stream(&mut out_buffer, &mut in_buffer)
.map_err(zstd_safe::get_error_name)
.unwrap();
}
loop {
if cstream
.end_stream(&mut out_buffer)
.map_err(zstd_safe::get_error_name)
.unwrap()
== 0
{
break;
}
}
Vec::from(out_buffer.as_slice())
}
#[cfg(feature = "seekable")]
fn check_seekable_decompression<F>(mut decompress: F)
where
F: FnMut(&mut [u8], u64) -> zstd_safe::SafeResult,
{
let mut buffer = std::vec![0u8; 64];
let written = decompress(&mut buffer[..], 0)
.map_err(zstd_safe::get_error_name)
.unwrap();
let decompressed = &buffer[..written];
assert!(INPUT.starts_with(decompressed));
assert_eq!(decompressed.len(), 64);
let mut buffer = std::vec![0u8; 256];
let written = decompress(&mut buffer[..], 0)
.map_err(zstd_safe::get_error_name)
.unwrap();
let decompressed = &buffer[..written];
assert_eq!(INPUT, decompressed);
}
#[cfg(feature = "std")]
#[test]
fn test_context_poisoned_by_an_error() {
use zstd_safe::{DCtx, InBuffer, OutBuffer, ResetDirective};
let mut dctx = DCtx::create();
let mut buffer = std::vec![0u8; 1024];
let garbage = [0xFFu8; 64];
let first = {
let mut input = InBuffer::around(&garbage[..]);
let mut output = OutBuffer::around(&mut buffer[..]);
dctx.decompress_stream(&mut output, &mut input).unwrap_err()
};
let mut compressed =
Vec::with_capacity(zstd_safe::compress_bound(INPUT.len()));
zstd_safe::compress(&mut compressed, INPUT, 3)
.map_err(zstd_safe::get_error_name)
.unwrap();
let second = {
let mut input = InBuffer::around(&compressed[..]);
let mut output = OutBuffer::around(&mut buffer[..]);
let err = dctx.decompress_stream(&mut output, &mut input).unwrap_err();
assert_eq!(input.pos(), 0, "the poisoned context consumed input");
assert_eq!(output.pos(), 0, "the poisoned context produced output");
err
};
assert_eq!(
zstd_safe::get_error_name(first),
zstd_safe::get_error_name(second)
);
dctx.reset(ResetDirective::SessionOnly)
.map_err(zstd_safe::get_error_name)
.unwrap();
let written = {
let mut input = InBuffer::around(&compressed[..]);
let mut output = OutBuffer::around(&mut buffer[..]);
dctx.decompress_stream(&mut output, &mut input)
.map_err(zstd_safe::get_error_name)
.unwrap();
output.pos()
};
assert_eq!(&buffer[..written], INPUT);
}
#[test]
fn test_poison_tracking() {
use crate::Poison;
let mut poison = Poison::default();
assert!(poison.guard().is_ok());
assert_eq!(poison.record(Ok(3)), Ok(3));
assert!(poison.guard().is_ok());
assert_eq!(poison.record(Err(42)), Err(42));
assert_eq!(poison.guard(), Err(42));
assert_eq!(poison.record(Ok(1)), Ok(1));
assert_eq!(poison.guard(), Err(42));
poison.clear();
assert!(poison.guard().is_ok());
}
#[cfg(all(feature = "std", feature = "experimental"))]
#[test]
#[allow(deprecated)] fn test_cloning_poisoned_contexts_returns_the_original_error() {
use crate::{CCtx, DCtx, InBuffer, OutBuffer};
let mut dctx = DCtx::create();
let mut output = [0; 1024];
let error = dctx
.decompress_stream(
&mut OutBuffer::around(&mut output[..]),
&mut InBuffer::around(&[0xff; 64]),
)
.unwrap_err();
assert_eq!(dctx.try_clone().err(), Some(error));
let mut cctx = CCtx::create();
cctx.2.record(Err(error)).unwrap_err();
assert_eq!(cctx.try_clone(None).err(), Some(error));
}
#[cfg(all(feature = "std", feature = "experimental"))]
#[test]
#[allow(deprecated)] fn test_cloning_initialized_compression_context() {
use crate::{parse_code, zstd_sys, CCtx, DCtx};
let cctx = CCtx::create();
parse_code(unsafe { zstd_sys::ZSTD_compressBegin(cctx.0.as_ptr(), 3) })
.unwrap();
let mut cloned = cctx.try_clone(Some(INPUT.len() as u64)).unwrap();
drop(cctx);
let mut compressed =
Vec::with_capacity(crate::compress_bound(INPUT.len()));
cloned.compress(&mut compressed, INPUT, 3).unwrap();
let mut decoded = Vec::with_capacity(INPUT.len());
DCtx::create()
.decompress(&mut decoded, &compressed)
.unwrap();
assert_eq!(decoded, INPUT);
}
#[cfg(all(feature = "std", feature = "with-rust-allocator"))]
#[test]
fn test_custom_allocator_dictionaries_interoperate_with_c_dictionaries() {
use crate::{zstd_sys, CCtx, CDict, DCtx};
use core::{marker::PhantomData, ptr::NonNull};
for dict_size in [512, 4096] {
let dict: Vec<u8> = (0..=255u8).cycle().take(dict_size).collect();
for level in [-3, 0, 3, 19] {
let rust_dict = CDict::create(&dict, level);
let c_dict = CDict(
NonNull::new(unsafe {
zstd_sys::ZSTD_createCDict(
crate::ptr_void(&dict),
dict.len(),
level,
)
})
.unwrap(),
PhantomData,
);
for input_size in [256, 256 * 1024] {
let input: Vec<u8> =
dict.iter().copied().cycle().take(input_size).collect();
let mut compressed =
Vec::with_capacity(crate::compress_bound(input_size));
let mut decoded = Vec::with_capacity(input_size);
for dictionary in [&rust_dict, &c_dict] {
CCtx::create()
.compress_using_cdict(
&mut compressed,
&input,
dictionary,
)
.unwrap();
DCtx::create()
.decompress_using_dict(
&mut decoded,
&compressed,
&dict,
)
.unwrap();
assert_eq!(decoded, input);
}
}
}
}
}