HEFGMMUMJ2GGJGF2BEKF7EUL24AO53WJTXKBJSEGT64AV3Y4TBZQC
use std::ffi::{CStr, CString};
use std::convert::TryInto;
use std::ffi::CStr;
use std::hash::Hasher;
use std::io::Read;
use std::{cmp, fmt, fs, io, marker, mem, ptr, slice, sync};
use twox_hash::{xxh3::hash64, XxHash64};
const BLOCKSIZELOG_MAX: usize = 17;
const BLOCKSIZE_MAX: usize = 1 << BLOCKSIZELOG_MAX;
const SEEKABLE_BUFF_SIZE: usize = BLOCKSIZE_MAX;
const SEEKABLE_MAXFRAMES: usize = 0x8000000;
const MAX_FRAME_DECOMPR_SIZE: u32 = 16;
const MAGIC_SKIPPABLE_START: usize = 0x184D2A50;
const SKIPPABLE_HEADER_SIZE: usize = 8;
const SEEK_TABLE_FOOTER_SIZE: usize = 9;
const SEEKABLE_MAGIC_NUMBER: usize = 0x8F92EAB1;
const FRAMELOG_STARTING_CAPACITY: usize = 16;
/// The type of seekable compressors.
pub struct SeekableCStream {
p: *mut ZSTD_seekable_CStream,
#[derive(Clone)]
pub struct FrameLogEntry {
c_size: u32,
d_size: u32,
checksum: u64,
}
/// The type of compressors.
pub struct FrameLog {
entries: Vec<FrameLogEntry>,
checksum_flag: u64,
seek_table_pos: u32,
seek_table_index: u32,
/// The type of compressors.
pub struct FrameLog {
p: *mut ZSTD_frameLog,
/// The type of seekable compressors.
pub struct SeekableCStream {
cstream: CStream,
framelog: FrameLog,
frame_c_size: u32,
frame_d_size: u32,
xxh_state: XxHash64,
max_frame_size: u32,
writing_seek_table: bool,
}
pub struct SeekEntry {
c_offset: usize,
d_offset: usize,
checksum: u64,
p: *mut ZSTD_seekable,
b: *mut R,
f: *mut libc::FILE,
marker: std::marker::PhantomData<&'a R>,
dstream: *mut ZSTD_DStream,
seek_table: SeekTable,
src: &'a mut R,
decompressed_offset: u64,
cur_frame: u32,
in_buff: [u8; SEEKABLE_BUFF_SIZE],
out_buff: [u8; SEEKABLE_BUFF_SIZE],
inn: ZSTD_inBuffer,
xxh_state: XxHash64,
// marker: marker::PhantomData<&'a R>, //TODO: do we need this marker?
unsafe {
if !self.p.is_null() {
ZSTD_seekable_free(self.p);
self.p = std::ptr::null_mut();
}
if !self.f.is_null() {
fclose(self.f);
self.f = std::ptr::null_mut();
}
if !self.b.is_null() {
let b: Box<R> = Box::from_raw(self.b);
std::mem::drop(b);
self.b = std::ptr::null_mut();
if !self.dstream.is_null() {
unsafe {
ZSTD_freeDStream(self.dstream);
self.dstream = ptr::null_mut();
impl Drop for FrameLog {
fn drop(&mut self) {
unsafe {
if !self.p.is_null() {
ZSTD_seekable_freeFrameLog(self.p);
self.p = std::ptr::null_mut()
}
}
}
}
unsafe extern "C" fn zstd_seekable_read<R: std::io::Read + std::io::Seek>(
unsafe extern "C" fn zstd_seekable_read<R: io::Read + io::Seek>(
pub fn new(level: usize, frame_size: usize) -> Result<Self, Error> {
unsafe {
let p = ZSTD_seekable_createCStream();
if p.is_null() {
return Err(Error::Null);
}
let result = ZSTD_seekable_initCStream(p, level as c_int, 1, frame_size as c_uint);
if ZSTD_isError(result) != 0 {
pub fn new(level: usize, frame_size: u32) -> Result<Self, Error> {
let cstream = unsafe { ZSTD_createCStream() };
if cstream.is_null() {
return Err(Error::Null);
}
if frame_size > MAX_FRAME_DECOMPR_SIZE {
return Err(Error::Other("FrameParameterUnsupported"));
} else {
let max_frame_size = if frame_size > 0 {
frame_size
} else {
MAX_FRAME_DECOMPR_SIZE
};
let result = unsafe { ZSTD_initCStream(cstream, level as c_int) };
if unsafe { ZSTD_isError(result) } != 0 {
Ok(SeekableCStream { p })
Ok(SeekableCStream {
cstream: CStream { p: cstream },
framelog: FrameLog {
entries: Vec::with_capacity(FRAMELOG_STARTING_CAPACITY),
checksum_flag: 1,
seek_table_pos: 0,
seek_table_index: 0,
},
frame_c_size: 0,
frame_d_size: 0,
xxh_state: XxHash64::with_seed(0),
max_frame_size,
writing_seek_table: false,
})
unsafe {
let mut input = ZSTD_inBuffer {
src: input.as_ptr() as *const c_void,
size: input.len() as size_t,
pos: 0,
};
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = ZSTD_seekable_compressStream(self.p, &mut output, &mut input);
if ZSTD_isError(result) != 0 {
return Err(Error::ZSTD(result));
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let len = cmp::min(input.len() as u32, self.max_frame_size - self.frame_d_size);
let mut in_tmp = ZSTD_inBuffer {
src: input.as_ptr() as *const c_void,
size: input.len() as size_t,
pos: 0,
};
if len > 0 {
let ret = unsafe { ZSTD_compressStream(self.cstream.p, &mut output, &mut in_tmp) };
if self.framelog.checksum_flag != 0 {
self.xxh_state
.write(unsafe { slice::from_raw_parts(in_tmp.src as *const _, in_tmp.pos) });
}
self.frame_c_size += output.pos as u32;
self.frame_d_size += in_tmp.pos as u32;
if unsafe { ZSTD_isError(ret) } != 0 {
return Err(Error::ZSTD(ret));
}
}
if self.max_frame_size == self.frame_d_size {
let ret = self.end_stream(&mut output)?;
if unsafe { ZSTD_isError(ret) } != 0 {
return Err(Error::ZSTD(ret));
Ok((output.pos as usize, input.pos as usize))
}
Ok((output.pos as usize, in_tmp.pos as usize))
}
fn end_frame(&mut self, mut output: ZSTD_outBuffer) -> Result<usize, Error> {
let prev_out_pos = output.pos;
let ret = unsafe { ZSTD_endStream(self.cstream.p, &mut output) };
self.frame_c_size += (output.pos - prev_out_pos) as u32;
if ret != 0 {
return Ok(ret);
}
let checksum = if self.framelog.checksum_flag != 0 {
self.xxh_state.finish() & 0xFFFFFFFFu64
} else {
0
};
self.framelog
.log_frame(self.frame_c_size, self.frame_d_size, checksum)?;
self.frame_c_size = 0;
self.frame_d_size = 0;
unsafe { ZSTD_resetCStream(self.cstream.p, 0) };
if self.framelog.checksum_flag != 0 {
self.xxh_state = XxHash64::with_seed(0);
pub fn end_stream(&mut self, output: &mut [u8]) -> Result<usize, Error> {
unsafe {
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = ZSTD_seekable_endStream(self.p, &mut output);
if ZSTD_isError(result) != 0 {
return Err(Error::ZSTD(result));
pub fn end_stream(&mut self, output: &mut ZSTD_outBuffer) -> Result<usize, Error> {
if !self.writing_seek_table && self.frame_d_size != 0 {
let end_frame = self.end_frame(*output)?;
if unsafe { ZSTD_isError(end_frame) } != 0 {
return Err(Error::ZSTD(end_frame));
} else if end_frame != 0 {
return Ok(end_frame + self.framelog.seek_table_size());
unsafe {
let mut input = ZSTD_inBuffer {
src: input.as_ptr() as *const c_void,
size: input.len() as size_t,
pos: 0,
};
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = ZSTD_compressStream(self.p, &mut output, &mut input);
Ok((output.pos as usize, input.pos as usize, result as usize))
}
let mut input = ZSTD_inBuffer {
src: input.as_ptr() as *const c_void,
size: input.len() as size_t,
pos: 0,
};
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = unsafe { ZSTD_compressStream(self.p, &mut output, &mut input) };
Ok((output.pos as usize, input.pos as usize, result as usize))
unsafe {
let mut input = ZSTD_inBuffer {
src: input.as_ptr() as *const c_void,
size: input.len() as size_t,
pos: 0,
};
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = ZSTD_compressStream2(self.p, &mut output, &mut input, op as ZSTD_EndDirective);
Ok((output.pos as usize, input.pos as usize, result as usize))
}
let mut input = ZSTD_inBuffer {
src: input.as_ptr() as *const c_void,
size: input.len() as size_t,
pos: 0,
};
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = unsafe {
ZSTD_compressStream2(self.p, &mut output, &mut input, op as ZSTD_EndDirective)
};
Ok((output.pos as usize, input.pos as usize, result as usize))
unsafe {
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = ZSTD_flushStream(self.p, &mut output);
Ok((output.pos as usize, result as usize))
}
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = unsafe { ZSTD_flushStream(self.p, &mut output) };
Ok((output.pos as usize, result as usize))
unsafe {
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = ZSTD_endStream(self.p, &mut output);
if ZSTD_isError(result) != 0 {
return Err(Error::ZSTD(result));
}
Ok(output.pos as usize)
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let result = unsafe { ZSTD_endStream(self.p, &mut output) };
if unsafe { ZSTD_isError(result) } != 0 {
return Err(Error::ZSTD(result));
unsafe {
let mut input = ZSTD_inBuffer {
src: input.as_ptr() as *const c_void,
size: input.len() as size_t,
pos: 0,
};
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
let mut input = ZSTD_inBuffer {
src: input.as_ptr() as *const c_void,
size: input.len() as size_t,
pos: 0,
};
let mut output = ZSTD_outBuffer {
dst: output.as_mut_ptr() as *mut c_void,
size: output.len() as size_t,
pos: 0,
};
let _result = unsafe { ZSTD_decompressStream(self.p, &mut output, &mut input) };
Ok((output.pos as usize, input.pos as usize))
}
}
impl<'a, R: io::Read + io::Seek> Seekable<'a, R> {
fn make_seekable(source: &'a mut R, dstream: *mut ZSTD_DStream) -> Self {
Seekable {
dstream,
seek_table: SeekTable {
entries: Vec::new(),
checksum_flag: 1,
},
src: source,
decompressed_offset: 0,
cur_frame: 0,
in_buff: [0; SEEKABLE_BUFF_SIZE],
out_buff: [0; SEEKABLE_BUFF_SIZE],
inn: ZSTD_inBuffer {
src: ptr::null() as *const c_void,
size: 0,
impl<'a> Seekable<'a, ()> {
/// Initialise a decompressor with an input buffer.
pub fn init_buf(input: &'a [u8]) -> Result<Self, Error> {
unsafe {
let p = ZSTD_seekable_create();
if p.is_null() {
return Err(Error::Null);
}
let result =
ZSTD_seekable_initBuff(p, input.as_ptr() as *const c_void, input.len() as size_t);
if ZSTD_isError(result) != 0 {
return Err(Error::ZSTD(result));
}
Ok(Seekable {
p,
f: std::ptr::null_mut(),
b: std::ptr::null_mut(),
marker: std::marker::PhantomData,
})
let mut handle = self.src.take(SEEK_TABLE_FOOTER_SIZE as u64);
handle.read(&mut self.in_buff);
if slice_to_num(&self.in_buff[5..9])? as usize != SEEKABLE_MAGIC_NUMBER {
return Err(Error::Other("PrefixUnknown"));
}
let checksum_flag = (self.in_buff[4] >> 7) as usize;
if ((checksum_flag >> 2) & 0x1f) == 0 {
return Err(Error::Other("CorruptionDetected"));
}
let num_frames = slice_to_num(&self.in_buff[..4])? as usize;
let size_p_entry: usize = 8 + if checksum_flag != 0 { 4 } else { 0 };
let table_size = size_p_entry * num_frames;
let frame_size = table_size + SEEK_TABLE_FOOTER_SIZE + SKIPPABLE_HEADER_SIZE;
/// Initialise a decompressor with a file. This method opens the file, and dropping the resulting `Seekable` closes the file.
pub fn init_file(name_: &str) -> Result<Self, Error> {
unsafe {
let name = CString::new(name_).unwrap();
let f: *mut libc::FILE = fopen(name.as_ptr(), "rb\0".as_ptr() as *const c_char);
if f.is_null() {
return Err(Error::CouldNotOpenFile(name_.to_string()));
}
let p = ZSTD_seekable_create();
if p.is_null() {
return Err(Error::Null);
let mut remaining = frame_size as usize - SEEK_TABLE_FOOTER_SIZE;
let to_read = cmp::min(remaining, SEEKABLE_BUFF_SIZE);
self.src.seek(io::SeekFrom::End(-(frame_size as i64)));
handle = self.src.take(to_read as u64);
handle.read(&mut self.in_buff)?;
remaining -= to_read;
if slice_to_num(&self.in_buff[..4])? as usize != (MAGIC_SKIPPABLE_START | 0xE) {
return Err(Error::Other("PrefixUnknown"));
}
if slice_to_num(&self.in_buff[4..8])? as usize + SKIPPABLE_HEADER_SIZE != frame_size {
return Err(Error::Other("PrefixUnknown"));
}
let mut entries: Vec<SeekEntry> = Vec::with_capacity((num_frames + 1) as usize);
let (mut idx, mut pos) = (0usize, 8usize);
let (mut c_offset, mut d_offset) = (0, 0);
let buffer_ptr = (&mut self.in_buff).as_mut_ptr();
while idx < num_frames {
if pos + size_p_entry > SEEKABLE_BUFF_SIZE {
let offset = SEEKABLE_BUFF_SIZE;
let to_read = cmp::min(remaining, SEEKABLE_BUFF_SIZE - offset);
// FIXME: replace this pointer arithmetic with something safer
unsafe { (buffer_ptr.add(pos)).copy_to(buffer_ptr, offset) };
handle = self.src.take(to_read as u64);
// FIXME: if pointer arithmetic replace, this needs to change
handle
.read(unsafe { slice::from_raw_parts_mut(buffer_ptr.add(offset), to_read) })?;
remaining -= to_read;
pos = 0;
let result = ZSTD_seekable_initFile(p, f as *mut FILE);
if ZSTD_isError(result) != 0 {
return Err(Error::ZSTD(result));
entries[idx].c_offset = c_offset;
entries[idx].d_offset = d_offset;
c_offset += slice_to_num(&self.in_buff[pos..pos + 4])? as usize;
pos += 4;
d_offset += slice_to_num(&self.in_buff[pos..pos + 4])? as usize;
pos += 4;
if checksum_flag != 0 {
entries[idx].checksum = slice_to_num(&self.in_buff[pos..pos + 4])? as u64;
pos += 4;
impl<'a, R: std::io::Read + std::io::Seek> Seekable<'a, R> {
/// Initialise a decompressor with a file. This method opens the file, and dropping the resulting `Seekable` closes the file.
pub fn init(r: Box<R>) -> Result<Self, Error> {
unsafe {
let p = ZSTD_seekable_create();
if p.is_null() {
return Err(Error::Null);
}
let opaque = Box::into_raw(r) as *mut R;
let adv = ZSTD_seekable_customFile {
opaque: opaque as *mut c_void,
read: Some(zstd_seekable_read::<R>),
seek: Some(zstd_seekable_seek::<R>),
};
let result = ZSTD_seekable_initAdvanced(p, adv);
if ZSTD_isError(result) != 0 {
return Err(Error::ZSTD(result));
}
Ok(Seekable {
p,
f: std::ptr::null_mut(),
b: opaque,
marker: std::marker::PhantomData,
})
fn init_advanced(&mut self) -> Result<(), Error> {
self.load_seek_table()?;
self.decompressed_offset = u64::MAX;
self.cur_frame = u32::MAX;
let dstream_init = unsafe { ZSTD_initDStream(self.dstream) };
if unsafe { ZSTD_isError(dstream_init) } != 0 {
Err(Error::ZSTD(dstream_init))
} else {
Ok(())
pub fn into_inner(self) -> Box<R> {
unsafe { Box::from_raw(self.b) }
/// Decompress a single frame. This method internally calls `decompress`, and `dest` must be exactly the size of the uncompressed frame.
pub fn decompress_frame(&mut self, dest: &mut [u8], index: usize) -> Result<usize, Error> {
let dec_size = self.get_frame_decompressed_size(index)?;
if dest.len() < dec_size {
return Err(Error::Other("DestinationSizeTooSmall"));
}
self.decompress(
dest,
dec_size,
self.seek_table.entries[index].d_offset as u64,
)
pub fn decompress(&mut self, out: &mut [u8], offset: u64) -> Result<usize, Error> {
unsafe {
let result = ZSTD_seekable_decompress(
self.p,
out.as_mut_ptr() as *mut c_void,
out.len() as size_t,
offset,
);
if ZSTD_isError(result) != 0 {
return Err(Error::ZSTD(result));
pub fn decompress(
&mut self,
out: &mut [u8],
len: usize,
offset: u64,
) -> Result<usize, Error> {
let mut tgt_frame = self.seekable_offset_to_frame_index(offset);
let dst = out.as_mut_ptr();
loop {
if tgt_frame as usize != self.cur_frame as usize || offset != self.decompressed_offset {
self.decompressed_offset = self.seek_table.entries[tgt_frame].d_offset as u64;
self.cur_frame = tgt_frame as u32;
self.src.seek(io::SeekFrom::Start(
self.seek_table.entries[tgt_frame].c_offset as u64,
));
self.inn = ZSTD_inBuffer {
src: self.in_buff.as_ptr() as *const _ as *const c_void,
size: 0,
pos: 0,
};
self.xxh_state = XxHash64::with_seed(0);
unsafe { ZSTD_resetDStream(self.dstream) };
Ok(result as usize)
while self.decompressed_offset < offset + len as u64 {
let mut out_tmp = if self.decompressed_offset < offset {
ZSTD_outBuffer {
dst: self.out_buff.as_mut_ptr() as *mut c_void,
size: cmp::min(
SEEKABLE_BUFF_SIZE,
(offset - self.decompressed_offset) as size_t,
),
pos: 0,
}
} else {
ZSTD_outBuffer {
dst: dst as *mut c_void,
size: len,
pos: (self.decompressed_offset - offset) as size_t,
}
};
let prev_out_pos = out_tmp.pos;
let mut to_read =
unsafe { ZSTD_decompressStream(self.dstream, &mut out_tmp, &mut self.inn) };
if unsafe { ZSTD_isError(to_read) } != 0 {
return Err(Error::ZSTD(to_read));
}
if self.seek_table.checksum_flag != 0 {
// FIXME: replace this pointer arithmetic with something safer
let tmp: &[u8] = unsafe {
slice::from_raw_parts_mut(
(out_tmp.dst as *mut u8).add(prev_out_pos),
out_tmp.pos - prev_out_pos,
)
};
self.xxh_state.write(tmp)
}
self.decompressed_offset += (out_tmp.pos - prev_out_pos) as u64;
if to_read == 0 {
if self.seek_table.checksum_flag != 0
&& (self.xxh_state.finish() & 0xFFFFFFFFu64)
!= self.seek_table.entries[tgt_frame].checksum
{
return Err(Error::Other("CorruptionDetected"));
}
if self.decompressed_offset < offset + len as u64 {
tgt_frame = self.seekable_offset_to_frame_index(self.decompressed_offset);
}
break;
}
if self.inn.pos == self.inn.size {
to_read = cmp::min(to_read, SEEKABLE_BUFF_SIZE);
let mut handle = self.src.take(to_read as u64);
handle
.read(&mut self.in_buff)
.map_err(|_| Error::Other("Not able to read buffer"));
self.inn.size = to_read;
self.inn.pos = 0;
}
}
if self.decompressed_offset != offset + len as u64 {
break;
}
}
Ok(len)
}
}
impl<'a> Seekable<'a, io::Cursor<&'a mut [u8]>> {
/// Initialise a decompressor with an input buffer.
pub fn init_buf(input: &'a mut [u8]) -> Result<Self, Error> {
let dstream = unsafe { ZSTD_createDStream() };
if dstream.is_null() {
return Err(Error::Null);
let mut source = io::Cursor::new(input);
let mut seekable = Seekable::make_seekable(&mut source, dstream);
seekable.init_advanced()?;
Ok(seekable)
}
}
impl<'a> Seekable<'a, fs::File> {
/// Initialise a decompressor with a file. This method opens the file, and dropping the resulting `Seekable` closes the file.
pub fn init_file(name: &'a str) -> Result<Self, Error> {
let mut source = fs::File::create(name)?;
let mut seekable = Seekable::make_seekable(&mut source, ptr::null_mut());
seekable.init_advanced()?;
Ok(seekable)
}
}
impl<'a, R: io::Read + io::Seek> Seekable<'a, Box<R>> {
/// Initialise a decompressor with a file. This method opens the file, and dropping the resulting `Seekable` closes the file.
pub fn init(source: &'a mut Box<R>) -> Result<Self, Error> {
let source = &mut *source;
let mut seekable = Seekable::make_seekable(source, ptr::null_mut());
seekable.init_advanced()?;
Ok(seekable)
pub fn get_frame_compressed_offset(&self, frame_index: usize) -> c_ulonglong {
unsafe { ZSTD_seekable_getFrameCompressedOffset(self.p, frame_index as c_uint) }
pub fn get_frame_compressed_offset(&self, frame_index: usize) -> Result<usize, Error> {
if frame_index >= self.get_num_frames() {
Err(Error::Other("FrameIndexTooLarge"))
} else {
Ok(self.seek_table.entries[frame_index].c_offset)
}
pub fn get_frame_compressed_size(&self, frame_index: usize) -> usize {
unsafe { ZSTD_seekable_getFrameCompressedSize(self.p, frame_index as c_uint) as usize }
pub fn get_frame_compressed_size(&self, frame_index: usize) -> Result<usize, Error> {
if frame_index >= self.get_num_frames() {
Err(Error::Other("FrameIndexTooLarge"))
} else {
Ok(self.seek_table.entries[frame_index + 1].c_offset
- self.seek_table.entries[frame_index].c_offset)
}
pub fn get_frame_decompressed_offset(&self, frame_index: usize) -> u64 {
unsafe { ZSTD_seekable_getFrameDecompressedOffset(self.p, frame_index as c_uint) }
pub fn get_frame_decompressed_offset(&self, frame_index: usize) -> Result<usize, Error> {
if frame_index >= self.get_num_frames() {
Err(Error::Other("FrameIndexTooLarge"))
} else {
Ok(self.seek_table.entries[frame_index].d_offset)
}
pub fn get_frame_decompressed_size(&self, frame_index: usize) -> usize {
unsafe { ZSTD_seekable_getFrameDecompressedSize(self.p, frame_index as c_uint) as usize }
}
/// Decompress a single frame. This method internally calls `decompress`, and `dest` must be exactly the size of the uncompressed frame.
pub fn decompress_frame(&mut self, dest: &mut [u8], index: usize) -> usize {
unsafe {
ZSTD_seekable_decompressFrame(
self.p,
dest.as_mut_ptr() as *mut c_void,
dest.len() as size_t,
index as c_uint,
) as usize
pub fn get_frame_decompressed_size(&self, frame_index: usize) -> Result<usize, Error> {
if frame_index > self.get_num_frames() {
Err(Error::Other("FrameIndexTooLarge"))
} else {
Ok(self.seek_table.entries[frame_index + 1].d_offset
- self.seek_table.entries[frame_index].d_offset)
pub fn seekable_offset_to_frame_index(&mut self, offset: u64) -> usize {
unsafe { ZSTD_seekable_offsetToFrameIndex(self.p, offset) as usize }
pub fn seekable_offset_to_frame_index(&self, offset: u64) -> usize {
if offset >= self.seek_table.entries[self.get_num_frames()].d_offset as u64 {
return self.get_num_frames();
}
let (mut lo, mut hi) = (0, self.get_num_frames());
while lo + 1 < hi {
let mid = lo + ((hi - lo) >> 1);
if self.seek_table.entries[mid].d_offset as u64 <= offset {
lo = mid
} else {
hi = mid;
}
}
lo
impl Dst for [u8; 512] {
fn as_mut_ptr(&mut self) -> *mut u8 {
self.as_mut().as_mut_ptr()
}
fn as_slice(&self) -> &[u8] {
self.as_ref()
}
fn len(&self) -> usize {
512
}
fn new() -> Self {
unsafe { std::mem::MaybeUninit::uninit().assume_init() }
}
}
impl Dst for [u8; 1024] {
fn as_mut_ptr(&mut self) -> *mut u8 {
self.as_mut().as_mut_ptr()
}
fn as_slice(&self) -> &[u8] {
self.as_ref()
}
fn len(&self) -> usize {
1024
}
fn new() -> Self {
unsafe { std::mem::MaybeUninit::uninit().assume_init() }
}
}
impl Dst for [u8; 2048] {
fn as_mut_ptr(&mut self) -> *mut u8 {
self.as_mut().as_mut_ptr()
}
fn as_slice(&self) -> &[u8] {
self.as_ref()
}
fn len(&self) -> usize {
2048
}
fn new() -> Self {
unsafe { std::mem::MaybeUninit::uninit().assume_init() }
}
}
impl Dst for [u8; 4096] {
fn as_mut_ptr(&mut self) -> *mut u8 {
self.as_mut().as_mut_ptr()
}
fn as_slice(&self) -> &[u8] {
self.as_ref()
}
fn len(&self) -> usize {
4096
}
fn new() -> Self {
unsafe { std::mem::MaybeUninit::uninit().assume_init() }
}
}
fn compress_frame<D: Dst>(
src_ptr: *const u8,
src_len: size_t,
level: usize,
) -> Result<CompressedFrame<D>, Error> {
unsafe {
let mut dst = D::new();
let checksum = xxh64(src_ptr, src_len as c_int);
let ret = ZSTD_compress(
let ret = unsafe {
ZSTD_compress(
let p = unsafe { ZSTD_seekable_createFrameLog(1) };
assert!(!p.is_null());
FrameLog { p }
FrameLog {
entries: Vec::new(),
checksum_flag: 1,
seek_table_pos: 0,
seek_table_index: 0,
}
}
pub fn log_frame(&mut self, c_size: u32, d_size: u32, checksum: u64) -> Result<(), Error> {
if self.entries.len() == SEEKABLE_MAXFRAMES {
return Err(Error::Other("FrameIndexTooLarge"));
}
self.entries.push(FrameLogEntry {
c_size,
d_size,
checksum,
});
Ok(())
}
#[inline(always)]
pub fn seek_table_size(&self) -> usize {
let size_p_frame = 8 + if self.checksum_flag != 0 { 4 } else { 0 };
return SKIPPABLE_HEADER_SIZE + size_p_frame * self.entries.len() + SEEK_TABLE_FOOTER_SIZE;
pub fn log_frame<D: Dst>(&mut self, frame: &CompressedFrame<D>) -> usize {
unsafe {
ZSTD_seekable_logFrame(
self.p,
frame.dst_size as c_uint,
frame.src_size as c_uint,
frame.checksum,
) as usize
// FIXME: replace this pointer arithmetic with something safer
unsafe {
ptr::copy_nonoverlapping(
tmp.as_ptr().add(self.seek_table_pos as usize - offset) as *const usize,
output.dst.add(output.pos) as *mut usize,
len_write,
)
};
output.pos += len_write;
return if len_write < 4 {
self.seek_table_size() - self.seek_table_pos as usize
} else {
0
};
}
pub fn write_seek_table(&mut self, output: &mut ZSTD_outBuffer) -> Result<usize, Error> {
let size_p_frame = 8 + if self.checksum_flag != 0 { 4 } else { 0 };
let table_len = self.seek_table_size();
let mut ret: usize = self.stwrite32(output, MAGIC_SKIPPABLE_START | 0xE, 0);
if ret != 0 {
return Ok(ret);
};
// assert!(table_len <= usize::MAX);
ret = self.stwrite32(output, table_len - SKIPPABLE_HEADER_SIZE, 4);
if ret != 0 {
return Ok(ret);
};
let mut i = self.seek_table_index as usize;
while i < self.entries.len() {
let start = SKIPPABLE_HEADER_SIZE + size_p_frame * i;
// assert!(start + 8 <= usize::MAX);
ret = self.stwrite32(output, self.entries[i].c_size as usize, start);
if ret != 0 {
return Ok(ret);
};
ret = self.stwrite32(output, self.entries[i].d_size as usize, start + 4);
if ret != 0 {
return Ok(ret);
};
if self.checksum_flag != 0 {
ret = self.stwrite32(output, self.entries[i].checksum as usize, start + 8);
if ret != 0 {
return Ok(ret);
};
}
pub fn parallel_compress<W: std::io::Write, D: Dst + 'static>(
src: &[u8],
ret = self.stwrite32(
output,
self.entries.len(),
table_len - SEEK_TABLE_FOOTER_SIZE,
);
if ret != 0 {
return Ok(ret);
};
if output.size - output.pos < 1 {
return Ok(table_len - self.seek_table_pos as usize);
}
if (self.seek_table_pos as usize) < table_len {
let sfd = (self.checksum_flag << 7) as u8;
let array = unsafe { slice::from_raw_parts_mut(output.dst as *mut u8, output.size) };
array
.get_mut(output.pos)
.and_then(|el| Some(*el = sfd))
.ok_or(Error::Other("index out of bounds"));
output.pos += 1;
}
ret = self.stwrite32(output, SEEKABLE_MAGIC_NUMBER, table_len - 4);
if ret != 0 {
return Ok(ret);
};
if self.seek_table_pos as usize != table_len {
Err(Error::Other("Generic error from ZSTD"))
} else {
Ok(0)
}
}
}
pub fn parallel_compress<W: io::Write, D: Dst + 'static>(
src: &'static [u8],
let frames: Vec<CompressedFrame<D>> = unsafe {
let mut frames = Vec::with_capacity(n);
frames.set_len(n);
for (i, frame) in rx.iter().take(n) {
frames[i] = frame
}
frames
};
let mut frames: Vec<CompressedFrame<D>> = Vec::with_capacity(n);
unsafe { frames.set_len(n) };
for (i, frame) in rx.iter().take(n) {
frames[i] = frame?;
}
use libc::{c_void, size_t, c_int, c_uint, c_char, c_ulonglong};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ZSTD_seekable_CStream {
_unused: [u8; 0],
}
use libc::{c_char, c_int, c_longlong, c_uint, c_void, size_t};
}
pub type ZSTD_seekable_read = ::std::option::Option<
unsafe extern "C" fn(
opaque: *mut ::std::os::raw::c_void,
buffer: *mut ::std::os::raw::c_void,
n: size_t,
) -> ::std::os::raw::c_int,
>;
pub type ZSTD_seekable_seek = ::std::option::Option<
unsafe extern "C" fn(
opaque: *mut ::std::os::raw::c_void,
offset: ::std::os::raw::c_longlong,
origin: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ZSTD_seekable_customFile {
pub opaque: *mut ::std::os::raw::c_void,
pub read: ZSTD_seekable_read,
pub seek: ZSTD_seekable_seek,
pub fn ZSTD_seekable_createCStream() -> *mut ZSTD_seekable_CStream;
pub fn ZSTD_seekable_freeCStream(zcs: *mut ZSTD_seekable_CStream) -> size_t;
pub fn ZSTD_seekable_initCStream(
zcs: *mut ZSTD_seekable_CStream,
compressionLevel: c_int,
checksumFlag: c_int,
maxFrameSize: c_uint,
) -> size_t;
pub fn ZSTD_seekable_compressStream(
zcs: *mut ZSTD_seekable_CStream,
output: *mut ZSTD_outBuffer,
input: *mut ZSTD_inBuffer,
) -> size_t;
pub fn ZSTD_seekable_endStream(
zcs: *mut ZSTD_seekable_CStream,
output: *mut ZSTD_outBuffer,
) -> size_t;
pub fn ZSTD_seekable_createFrameLog(checksumFlag: c_int) -> *mut ZSTD_frameLog;
pub fn ZSTD_seekable_freeFrameLog(fl: *mut ZSTD_frameLog) -> size_t;
pub fn ZSTD_seekable_logFrame(
fl: *mut ZSTD_frameLog,
compressedSize: c_uint,
decompressedSize: c_uint,
checksum: c_uint,
) -> size_t;
pub fn ZSTD_seekable_writeSeekTable(
fl: *mut ZSTD_frameLog,
output: *mut ZSTD_outBuffer,
) -> size_t;
pub fn ZSTD_seekable_create() -> *mut ZSTD_seekable;
pub fn ZSTD_seekable_free(zs: *mut ZSTD_seekable) -> size_t;
pub fn ZSTD_seekable_initBuff(
zs: *mut ZSTD_seekable,
src: *const c_void,
srcSize: size_t,
) -> size_t;
pub fn ZSTD_seekable_initFile(zs: *mut ZSTD_seekable, src: *mut libc::FILE) -> size_t;
pub fn ZSTD_seekable_decompress(
zs: *mut ZSTD_seekable,
dst: *mut c_void,
dstSize: size_t,
offset: c_ulonglong,
) -> size_t;
pub fn ZSTD_seekable_decompressFrame(
zs: *mut ZSTD_seekable,
dst: *mut c_void,
dstSize: size_t,
frameIndex: c_uint,
) -> size_t;
pub fn ZSTD_seekable_getNumFrames(zs: *mut ZSTD_seekable) -> c_uint;
pub fn ZSTD_seekable_getFrameCompressedOffset(
zs: *mut ZSTD_seekable,
frameIndex: c_uint,
) -> c_ulonglong;
pub fn ZSTD_seekable_getFrameDecompressedOffset(
zs: *mut ZSTD_seekable,
frameIndex: c_uint,
) -> c_ulonglong;
pub fn ZSTD_seekable_getFrameCompressedSize(
zs: *mut ZSTD_seekable,
frameIndex: c_uint,
) -> size_t;
pub fn ZSTD_seekable_getFrameDecompressedSize(
zs: *mut ZSTD_seekable,
frameIndex: c_uint,
) -> size_t;
pub fn ZSTD_seekable_offsetToFrameIndex(
zs: *mut ZSTD_seekable,
offset: c_ulonglong,
) -> c_uint;
pub fn ZSTD_seekable_initAdvanced(
zs: *mut ZSTD_seekable,
src: ZSTD_seekable_customFile,
) -> size_t;
pub fn ZSTD_initCStream(
zcs: *mut ZSTD_CStream,
compressionLevel: c_int,
) -> size_t;
pub fn ZSTD_initCStream(zcs: *mut ZSTD_CStream, compressionLevel: c_int) -> size_t;
"src/bindings.rs",
"xxh64.c",
"zstd/contrib/seekable_format/zstd_seekable.h",
"zstd/contrib/seekable_format/zstdseek_compress.c",
"zstd/contrib/seekable_format/zstdseek_decompress.c",
"zstd/lib/common/mem.h",
"zstd/lib/common/xxhash.h",
"zstd/lib/zstd.h",
"zstd/lib/common/zstd_errors.h",
"src/bindings.rs"