zstd_seekable/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#[macro_use]
5extern crate thiserror;
6mod bindings;
7use bindings::*;
8
9#[cfg(not(target_family = "wasm"))]
10use libc::*;
11#[cfg(not(target_family = "wasm"))]
12use std::ffi::{CString};
13use std::ffi::{c_void, CStr};
14
15#[cfg(target_family = "wasm")]
16type c_longlong = i64;
17
18pub fn version() -> &'static CStr {
19    unsafe {
20        CStr::from_ptr(ZSTD_versionString())
21    }
22}
23
24pub fn out_size() -> size_t {
25    unsafe { ZSTD_DStreamOutSize() }
26}
27
28/// The type of seekable compressors.
29pub struct SeekableCStream {
30    p: *mut ZSTD_seekable_CStream,
31}
32
33/// The type of compressors.
34pub struct CStream {
35    p: *mut ZSTD_CStream,
36}
37
38/// The type of compressors.
39pub struct FrameLog {
40    p: *mut ZSTD_frameLog,
41}
42
43/// The type of decompressors.
44#[cfg(not(target_family = "wasm"))]
45pub struct Seekable<'a, R> {
46    p: *mut ZSTD_seekable,
47    b: *mut R,
48    f: *mut libc::FILE,
49    marker: std::marker::PhantomData<&'a R>,
50}
51
52#[cfg(target_family = "wasm")]
53pub struct Seekable<'a, R> {
54    p: *mut ZSTD_seekable,
55    b: *mut R,
56    f: *mut std::ffi::c_void,
57    marker: std::marker::PhantomData<&'a R>,
58}
59
60unsafe impl<R> Send for Seekable<'static, R> {}
61unsafe impl Send for SeekableCStream {}
62unsafe impl Send for FrameLog {}
63unsafe impl<D: Dst> Send for CompressedFrame<D> {}
64
65#[derive(Error)]
66pub enum Error {
67    Null,
68    CouldNotOpenFile(String),
69    ZSTD(size_t),
70    Io(#[from] std::io::Error),
71}
72
73impl std::fmt::Debug for Error {
74    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
75        match *self {
76            Error::Null => write!(fmt, "Null"),
77            Error::Io(ref e) => e.fmt(fmt),
78            Error::CouldNotOpenFile(ref f) => write!(fmt, "Could not open {}", f),
79            Error::ZSTD(r) => unsafe {
80                let error = CStr::from_ptr(ZSTD_getErrorName(r));
81                write!(fmt, "ZSTD({:?})", error)
82            },
83        }
84    }
85}
86
87impl std::fmt::Display for Error {
88    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
89        write!(fmt, "{:?}", self)
90    }
91}
92
93impl Drop for SeekableCStream {
94    fn drop(&mut self) {
95        unsafe {
96            if !self.p.is_null() {
97                ZSTD_seekable_freeCStream(self.p);
98                self.p = std::ptr::null_mut();
99            }
100        }
101    }
102}
103
104impl Drop for CStream {
105    fn drop(&mut self) {
106        unsafe {
107            if !self.p.is_null() {
108                ZSTD_freeCStream(self.p);
109                self.p = std::ptr::null_mut();
110            }
111        }
112    }
113}
114
115
116impl<'a, R> Drop for Seekable<'a, R> {
117    fn drop(&mut self) {
118        unsafe {
119            if !self.p.is_null() {
120                ZSTD_seekable_free(self.p);
121                self.p = std::ptr::null_mut();
122            }
123            if !self.f.is_null() {
124                #[cfg(target_family = "wasm")]
125                fn fclose(_: *mut c_void) {}
126                fclose(self.f);
127                self.f = std::ptr::null_mut();
128            }
129            if !self.b.is_null() {
130                let b: Box<R> = Box::from_raw(self.b);
131                std::mem::drop(b);
132                self.b = std::ptr::null_mut();
133            }
134        }
135    }
136}
137
138impl Drop for FrameLog {
139    fn drop(&mut self) {
140        unsafe {
141            if !self.p.is_null() {
142                ZSTD_seekable_freeFrameLog(self.p);
143                self.p = std::ptr::null_mut()
144            }
145        }
146    }
147}
148
149unsafe extern "C" fn zstd_seekable_read<R: std::io::Read + std::io::Seek>(
150    opaque: *mut c_void,
151    buffer: *mut c_void,
152    n: size_t,
153) -> c_int {
154    let mut b: Box<R> = Box::from_raw(opaque as *mut R);
155    let s = std::slice::from_raw_parts_mut(buffer as *mut u8, n as usize);
156    let result = if b.read_exact(s).is_ok() { 0 } else { 1 };
157    std::mem::forget(b);
158    result
159}
160
161#[cfg(target_family = "wasm")]
162const SEEK_SET: c_int = 0;
163#[cfg(target_family = "wasm")]
164const SEEK_CUR: c_int = 1;
165// #[cfg(target_family = "wasm")]
166// const SEEK_END: c_int = 2;
167
168unsafe extern "C" fn zstd_seekable_seek<R: std::io::Read + std::io::Seek>(
169    opaque: *mut c_void,
170    offset: c_longlong,
171    origin: c_int,
172) -> c_int {
173    let mut b: Box<R> = Box::from_raw(opaque as *mut R);
174    use std::io::SeekFrom;
175    let origin = if origin == SEEK_SET {
176        SeekFrom::Start(offset as u64)
177    } else if origin == SEEK_CUR {
178        SeekFrom::Current(offset as i64)
179    } else {
180        SeekFrom::End(offset as i64)
181    };
182    let result = if b.seek(origin).is_ok() { 0 } else { 1 };
183    std::mem::forget(b);
184    result
185}
186
187impl SeekableCStream {
188    /// Create a compressor with the given level and frame size. When seeking in the file, frames are decompressed one by one, so this should be chosen of a size similar to the chunks that will be decompressed.
189    pub fn new(level: usize, frame_size: usize) -> Result<Self, Error> {
190        unsafe {
191            let p = ZSTD_seekable_createCStream();
192            if p.is_null() {
193                return Err(Error::Null);
194            }
195            let result = ZSTD_seekable_initCStream(p, level as c_int, 1, frame_size as c_uint);
196            if ZSTD_isError(result) != 0 {
197                return Err(Error::ZSTD(result));
198            }
199            Ok(SeekableCStream { p })
200        }
201    }
202    /// Compress one chunk of input, and write it into the output. The `output` array must be large enough to hold the result. If successful, this function returns two integers `(out_pos, in_pos)`, where `out_pos` is the number of bytes written in `output`, and `in_pos` is the number of input bytes consumed.
203    pub fn compress(&mut self, output: &mut [u8], input: &[u8]) -> Result<(usize, usize), Error> {
204        unsafe {
205            let mut input = ZSTD_inBuffer {
206                src: input.as_ptr() as *const c_void,
207                size: input.len() as size_t,
208                pos: 0,
209            };
210            let mut output = ZSTD_outBuffer {
211                dst: output.as_mut_ptr() as *mut c_void,
212                size: output.len() as size_t,
213                pos: 0,
214            };
215            let result = ZSTD_seekable_compressStream(self.p, &mut output, &mut input);
216            if ZSTD_isError(result) != 0 {
217                return Err(Error::ZSTD(result));
218            }
219            Ok((output.pos as usize, input.pos as usize))
220        }
221    }
222
223    /// Finish writing the message, i.e. write the remaining pending block.
224    pub fn end_stream(&mut self, output: &mut [u8]) -> Result<usize, Error> {
225        unsafe {
226            let mut output = ZSTD_outBuffer {
227                dst: output.as_mut_ptr() as *mut c_void,
228                size: output.len() as size_t,
229                pos: 0,
230            };
231            let result = ZSTD_seekable_endStream(self.p, &mut output);
232            if ZSTD_isError(result) != 0 {
233                return Err(Error::ZSTD(result));
234            }
235            Ok(output.pos as usize)
236        }
237    }
238}
239
240impl CStream {
241    pub fn new(level: usize) -> Result<Self, Error> {
242        unsafe {
243            let p = ZSTD_createCStream();
244            if p.is_null() {
245                return Err(Error::Null);
246            }
247            let result = ZSTD_initCStream(p, level as c_int);
248            if ZSTD_isError(result) != 0 {
249                return Err(Error::ZSTD(result));
250            }
251            Ok(CStream { p })
252        }
253    }
254
255    pub fn in_size() -> usize {
256        unsafe { ZSTD_CStreamInSize() as usize }
257    }
258
259    pub fn out_size() -> usize {
260        unsafe { ZSTD_CStreamOutSize() as usize }
261    }
262
263    /// Compress one chunk of input, and write it into the output. The `output` array must be large enough to hold the result. If successful, this function returns three integers `(out_pos, in_pos, next_read_size)`, where `out_pos` is the number of bytes written in `output`, `in_pos` is the number of input bytes consumed, and `next_read_size` is a hint for the next read size.
264    pub fn compress(
265        &mut self,
266        output: &mut [u8],
267        input: &[u8],
268    ) -> Result<(usize, usize, usize), Error> {
269        unsafe {
270            let mut input = ZSTD_inBuffer {
271                src: input.as_ptr() as *const c_void,
272                size: input.len() as size_t,
273                pos: 0,
274            };
275            let mut output = ZSTD_outBuffer {
276                dst: output.as_mut_ptr() as *mut c_void,
277                size: output.len() as size_t,
278                pos: 0,
279            };
280            let result = ZSTD_compressStream(self.p, &mut output, &mut input);
281            Ok((output.pos as usize, input.pos as usize, result as usize))
282        }
283    }
284
285    pub fn compress2(
286        &mut self,
287        output: &mut [u8],
288        input: &[u8],
289        op: EndDirective,
290    ) -> Result<(usize, usize, usize), Error> {
291        unsafe {
292            let mut input = ZSTD_inBuffer {
293                src: input.as_ptr() as *const c_void,
294                size: input.len() as size_t,
295                pos: 0,
296            };
297            let mut output = ZSTD_outBuffer {
298                dst: output.as_mut_ptr() as *mut c_void,
299                size: output.len() as size_t,
300                pos: 0,
301            };
302            let result = ZSTD_compressStream2(self.p, &mut output, &mut input, op as ZSTD_EndDirective);
303            Ok((output.pos as usize, input.pos as usize, result as usize))
304        }
305    }
306
307    pub fn flush(&mut self, output: &mut [u8]) -> Result<(usize, usize), Error> {
308        unsafe {
309            let mut output = ZSTD_outBuffer {
310                dst: output.as_mut_ptr() as *mut c_void,
311                size: output.len() as size_t,
312                pos: 0,
313            };
314            let result = ZSTD_flushStream(self.p, &mut output);
315            Ok((output.pos as usize, result as usize))
316        }
317    }
318
319    /// Finish writing the message, i.e. write the remaining pending block.
320    pub fn end(&mut self, output: &mut [u8]) -> Result<usize, Error> {
321        unsafe {
322            let mut output = ZSTD_outBuffer {
323                dst: output.as_mut_ptr() as *mut c_void,
324                size: output.len() as size_t,
325                pos: 0,
326            };
327            let result = ZSTD_endStream(self.p, &mut output);
328            if ZSTD_isError(result) != 0 {
329                return Err(Error::ZSTD(result));
330            }
331            Ok(output.pos as usize)
332        }
333    }
334}
335
336pub enum EndDirective {
337    Continue = 0,
338    Flush = 1,
339    End = 2,
340}
341
342pub struct DStream {
343    p: *mut ZSTD_DStream,
344}
345
346unsafe impl Send for DStream {}
347
348impl Drop for DStream {
349    fn drop(&mut self) {
350        unsafe {
351            ZSTD_freeDStream(self.p);
352            self.p = std::ptr::null_mut();
353        }
354    }
355}
356
357impl DStream {
358    pub fn new() -> Result<Self, Error> {
359        unsafe {
360            let p = ZSTD_createDStream();
361            if p.is_null() {
362                return Err(Error::Null);
363            }
364            let result = ZSTD_initDStream(p);
365            if ZSTD_isError(result) != 0 {
366                return Err(Error::ZSTD(result));
367            }
368            Ok(DStream { p })
369        }
370    }
371
372    pub fn decompress(&mut self, output: &mut [u8], input: &[u8]) -> Result<(usize, usize), Error> {
373        unsafe {
374            let mut input = ZSTD_inBuffer {
375                src: input.as_ptr() as *const c_void,
376                size: input.len() as size_t,
377                pos: 0,
378            };
379            let mut output = ZSTD_outBuffer {
380                dst: output.as_mut_ptr() as *mut c_void,
381                size: output.len() as size_t,
382                pos: 0,
383            };
384            let _result = ZSTD_decompressStream(self.p, &mut output, &mut input);
385            Ok((output.pos as usize, input.pos as usize))
386        }
387    }
388}
389
390impl<'a> Seekable<'a, ()> {
391    /// Initialise a decompressor with an input buffer.
392    pub fn init_buf(input: &'a [u8]) -> Result<Self, Error> {
393        unsafe {
394            let p = ZSTD_seekable_create();
395            if p.is_null() {
396                return Err(Error::Null);
397            }
398            let result =
399                ZSTD_seekable_initBuff(p, input.as_ptr() as *const c_void, input.len() as size_t);
400            if ZSTD_isError(result) != 0 {
401                return Err(Error::ZSTD(result));
402            }
403            Ok(Seekable {
404                p,
405                f: std::ptr::null_mut(),
406                b: std::ptr::null_mut(),
407                marker: std::marker::PhantomData,
408            })
409        }
410    }
411
412    /// Initialise a decompressor with a file. This method opens the file, and dropping the resulting `Seekable` closes the file.
413    #[cfg(not(target_family = "wasm"))]
414    pub fn init_file(name_: &str) -> Result<Self, Error> {
415        unsafe {
416            let name = CString::new(name_).unwrap();
417            let f: *mut libc::FILE = fopen(name.as_ptr(), "rb\0".as_ptr() as *const c_char);
418            if f.is_null() {
419                return Err(Error::CouldNotOpenFile(name_.to_string()));
420            }
421            let p = ZSTD_seekable_create();
422            if p.is_null() {
423                return Err(Error::Null);
424            }
425            let result = ZSTD_seekable_initFile(p, f as *mut FILE);
426            if ZSTD_isError(result) != 0 {
427                return Err(Error::ZSTD(result));
428            }
429            Ok(Seekable {
430                p,
431                f,
432                b: std::ptr::null_mut(),
433                marker: std::marker::PhantomData,
434            })
435        }
436    }
437}
438
439impl<'a, R: std::io::Read + std::io::Seek> Seekable<'a, R> {
440    /// Initialise a decompressor with a file. This method opens the file, and dropping the resulting `Seekable` closes the file.
441    pub fn init(r: Box<R>) -> Result<Self, Error> {
442        unsafe {
443            let p = ZSTD_seekable_create();
444            if p.is_null() {
445                return Err(Error::Null);
446            }
447            let opaque = Box::into_raw(r) as *mut R;
448            let adv = ZSTD_seekable_customFile {
449                opaque: opaque as *mut c_void,
450                read: Some(zstd_seekable_read::<R>),
451                seek: Some(zstd_seekable_seek::<R>),
452            };
453            let result = ZSTD_seekable_initAdvanced(p, adv);
454            if ZSTD_isError(result) != 0 {
455                return Err(Error::ZSTD(result));
456            }
457            Ok(Seekable {
458                p,
459                f: std::ptr::null_mut(),
460                b: opaque,
461                marker: std::marker::PhantomData,
462            })
463        }
464    }
465
466    pub fn into_inner(self) -> Box<R> {
467        unsafe { Box::from_raw(self.b) }
468    }
469}
470
471impl<'a, R> Seekable<'a, R> {
472    /// Decompress starting from an offset. The length of `out` must
473    /// be at most the size of the decompressed output.
474    ///
475    /// This function finds the correct frame to start with, and takes
476    /// care of decompressing multiple frames in a row.
477    pub fn decompress(&mut self, out: &mut [u8], offset: u64) -> Result<usize, Error> {
478        unsafe {
479            let result = ZSTD_seekable_decompress(
480                self.p,
481                out.as_mut_ptr() as *mut c_void,
482                out.len() as size_t,
483                offset,
484            );
485            if ZSTD_isError(result) != 0 {
486                return Err(Error::ZSTD(result));
487            }
488            Ok(result as usize)
489        }
490    }
491
492    /// Number of frames in the message.
493    pub fn get_num_frames(&self) -> usize {
494        unsafe { ZSTD_seekable_getNumFrames(self.p) as usize }
495    }
496    /// Offset of the frame in the compressed data.
497    pub fn get_frame_compressed_offset(&self, frame_index: usize) -> c_ulonglong {
498        unsafe { ZSTD_seekable_getFrameCompressedOffset(self.p, frame_index as c_uint) }
499    }
500    /// Size of the frame in the compressed data.
501    pub fn get_frame_compressed_size(&self, frame_index: usize) -> usize {
502        unsafe { ZSTD_seekable_getFrameCompressedSize(self.p, frame_index as c_uint) as usize }
503    }
504    /// Offset of the frame in the decompressed data.
505    pub fn get_frame_decompressed_offset(&self, frame_index: usize) -> u64 {
506        unsafe { ZSTD_seekable_getFrameDecompressedOffset(self.p, frame_index as c_uint) }
507    }
508    /// Size of the frame in the decompressed data.
509    pub fn get_frame_decompressed_size(&self, frame_index: usize) -> usize {
510        unsafe { ZSTD_seekable_getFrameDecompressedSize(self.p, frame_index as c_uint) as usize }
511    }
512    /// Decompress a single frame. This method internally calls `decompress`, and `dest` must be exactly the size of the uncompressed frame.
513    pub fn decompress_frame(&mut self, dest: &mut [u8], index: usize) -> usize {
514        unsafe {
515            ZSTD_seekable_decompressFrame(
516                self.p,
517                dest.as_mut_ptr() as *mut c_void,
518                dest.len() as size_t,
519                index as c_uint,
520            ) as usize
521        }
522    }
523    /// Perform a binary search to find the frame containing the offset.
524    pub fn seekable_offset_to_frame_index(&mut self, offset: u64) -> usize {
525        unsafe { ZSTD_seekable_offsetToFrameIndex(self.p, offset) as usize }
526    }
527}
528
529pub trait Dst: Send {
530    fn as_mut_ptr(&mut self) -> *mut u8;
531    fn as_slice(&self) -> &[u8];
532    fn len(&self) -> usize;
533    fn new() -> Self;
534}
535impl Dst for [u8; 256] {
536    fn as_mut_ptr(&mut self) -> *mut u8 {
537        self.as_mut().as_mut_ptr()
538    }
539    fn as_slice(&self) -> &[u8] {
540        self.as_ref()
541    }
542    fn len(&self) -> usize {
543        256
544    }
545    fn new() -> Self {
546        [0; 256]
547    }
548}
549impl Dst for [u8; 512] {
550    fn as_mut_ptr(&mut self) -> *mut u8 {
551        self.as_mut().as_mut_ptr()
552    }
553    fn as_slice(&self) -> &[u8] {
554        self.as_ref()
555    }
556    fn len(&self) -> usize {
557        512
558    }
559    fn new() -> Self {
560        [0; 512]
561    }
562}
563impl Dst for [u8; 1024] {
564    fn as_mut_ptr(&mut self) -> *mut u8 {
565        self.as_mut().as_mut_ptr()
566    }
567    fn as_slice(&self) -> &[u8] {
568        self.as_ref()
569    }
570    fn len(&self) -> usize {
571        1024
572    }
573    fn new() -> Self {
574        [0; 1024]
575    }
576}
577impl Dst for [u8; 2048] {
578    fn as_mut_ptr(&mut self) -> *mut u8 {
579        self.as_mut().as_mut_ptr()
580    }
581    fn as_slice(&self) -> &[u8] {
582        self.as_ref()
583    }
584    fn len(&self) -> usize {
585        2048
586    }
587    fn new() -> Self {
588        [0; 2048]
589    }
590}
591impl Dst for [u8; 4096] {
592    fn as_mut_ptr(&mut self) -> *mut u8 {
593        self.as_mut().as_mut_ptr()
594    }
595    fn as_slice(&self) -> &[u8] {
596        self.as_ref()
597    }
598    fn len(&self) -> usize {
599        4096
600    }
601    fn new() -> Self {
602        [0; 4096]
603    }
604}
605
606pub struct CompressedFrame<D: Dst> {
607    src_size: size_t,
608    dst_size: size_t,
609    checksum: c_uint,
610    dst: D,
611}
612
613impl<D: Dst> CompressedFrame<D> {
614    fn as_slice(&self) -> &[u8] {
615        &self.dst.as_slice()[..self.dst_size as usize]
616    }
617}
618
619extern "C" {
620    fn xxh64(src: *const u8, len: c_int) -> c_uint;
621}
622
623fn compress_frame<D: Dst>(
624    src_ptr: *const u8,
625    src_len: size_t,
626    level: usize,
627) -> Result<CompressedFrame<D>, Error> {
628    unsafe {
629        let mut dst = D::new();
630        let checksum = xxh64(src_ptr, src_len as c_int);
631        let ret = ZSTD_compress(
632            dst.as_mut_ptr() as *mut c_void,
633            dst.len() as size_t,
634            src_ptr as *const c_void,
635            src_len,
636            level as c_int,
637        );
638        if ZSTD_isError(ret) != 0 {
639            return Err(Error::ZSTD(ret));
640        }
641        println!("{:?}", dst.as_slice());
642        Ok(CompressedFrame {
643            src_size: src_len,
644            dst_size: ret,
645            checksum,
646            dst,
647        })
648    }
649}
650
651impl FrameLog {
652    pub fn new() -> Self {
653        let p = unsafe { ZSTD_seekable_createFrameLog(1) };
654        assert!(!p.is_null());
655        FrameLog { p }
656    }
657
658    pub fn log_frame<D: Dst>(&mut self, frame: &CompressedFrame<D>) -> usize {
659        unsafe {
660            ZSTD_seekable_logFrame(
661                self.p,
662                frame.dst_size as c_uint,
663                frame.src_size as c_uint,
664                frame.checksum,
665            ) as usize
666        }
667    }
668
669    pub fn write_all<W: std::io::Write>(&self, mut w: W) -> Result<(), std::io::Error> {
670        let mut output = [0; 1024];
671        let mut output_ = ZSTD_outBuffer {
672            dst: output.as_mut_ptr() as *mut c_void,
673            size: 1024,
674            pos: 0,
675        };
676        unsafe {
677            while ZSTD_seekable_writeSeekTable(self.p, &mut output_) != 0 {
678                w.write_all(&output[..output_.pos as usize])?;
679                output_.pos = 0;
680            }
681            w.write_all(&output[..output_.pos as usize])?;
682        }
683        Ok(())
684    }
685}
686
687struct Chunk {
688    p: *const u8,
689    len: size_t,
690}
691
692unsafe impl Send for Chunk {}
693
694pub fn parallel_compress<W: std::io::Write, D: Dst + 'static>(
695    src: &[u8],
696    mut output: W,
697    level: usize,
698    jobs: usize,
699) -> Result<(), Error> {
700    use std::sync::mpsc::channel;
701    use threadpool::ThreadPool;
702
703    let chunk_size = 256;
704    let n = src.len() / chunk_size + if src.len() % 256 == 0 { 0 } else { 1 };
705    let pool = ThreadPool::new(jobs);
706
707    let (tx, rx) = channel();
708    for (i, chunk) in src.chunks(chunk_size).enumerate() {
709        let tx = tx.clone();
710        let chunk = Chunk {
711            p: chunk.as_ptr(),
712            len: chunk.len() as size_t,
713        };
714        pool.execute(move || {
715            let frame = compress_frame(chunk.p, chunk.len, level).unwrap();
716            tx.send((i, frame))
717                .expect("channel will be there waiting for the pool");
718        });
719    }
720
721    let frames: Vec<CompressedFrame<D>> = unsafe {
722        let mut frames = Vec::with_capacity(n);
723        frames.set_len(n);
724        for (i, frame) in rx.iter().take(n) {
725            frames[i] = frame
726        }
727        frames
728    };
729    let mut log = FrameLog::new();
730    for frame in frames.iter() {
731        output.write_all(frame.as_slice())?;
732        log.log_frame(frame);
733    }
734    log.write_all(&mut output)?;
735    Ok(())
736}
737
738#[test]
739fn test_par() {
740    let input = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut diam ante, sollicitudin a dolor et, volutpat elementum nulla. Etiam nec efficitur nibh, quis rutrum risus. Maecenas quis lorem malesuada, aliquet mi vel, viverra nunc. Donec et nulla sed velit sagittis varius. Suspendisse vestibulum, neque lobortis ornare vestibulum, orci turpis vulputate nisi, ut sodales neque purus eget magna. Nunc condimentum, diam eu consequat venenatis, est nisl semper lorem, et lobortis velit justo sed nulla. Nunc sit amet tempor nunc, vel posuere ipsum. Cras erat tortor, pulvinar ac pretium eu, auctor ac nibh. Duis iaculis porta magna, eu lobortis elit. Duis vitae massa eros. Nulla non magna accumsan, egestas quam sit amet, laoreet lectus.";
741    let mut output = Vec::new();
742    parallel_compress::<&mut Vec<u8>, [u8; 256]>(input, &mut output, 10, 4).unwrap();
743    let mut decomp = Vec::new();
744    let mut s = { Seekable::init_buf(&output).unwrap() };
745    for frame in 0..s.get_num_frames() {
746        let size = s.get_frame_decompressed_size(frame);
747        let n = decomp.len();
748        decomp.extend(std::iter::repeat(0).take(size));
749        s.decompress_frame(&mut decomp[n..], frame);
750    }
751    println!("{:?}", std::str::from_utf8(&decomp).unwrap());
752
753    assert_eq!(&input[..], &decomp[..])
754}
755
756#[test]
757fn test() {
758    let mut cstream = SeekableCStream::new(10, 256).unwrap();
759    let input = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut diam ante, sollicitudin a dolor et, volutpat elementum nulla. Etiam nec efficitur nibh, quis rutrum risus. Maecenas quis lorem malesuada, aliquet mi vel, viverra nunc. Donec et nulla sed velit sagittis varius. Suspendisse vestibulum, neque lobortis ornare vestibulum, orci turpis vulputate nisi, ut sodales neque purus eget magna. Nunc condimentum, diam eu consequat venenatis, est nisl semper lorem, et lobortis velit justo sed nulla. Nunc sit amet tempor nunc, vel posuere ipsum. Cras erat tortor, pulvinar ac pretium eu, auctor ac nibh. Duis iaculis porta magna, eu lobortis elit. Duis vitae massa eros. Nulla non magna accumsan, egestas quam sit amet, laoreet lectus.";
760    let mut input_pos = 0;
761    let mut output = vec![0; input.len()];
762    let mut output_pos = 0;
763    while input_pos < input.len() {
764        let (a, b) = cstream
765            .compress(&mut output[output_pos..], &input[input_pos..])
766            .unwrap();
767        output_pos += a;
768        input_pos += b;
769    }
770    while let Ok(n) = cstream.end_stream(&mut output[output_pos..]) {
771        if n == 0 {
772            break;
773        }
774        output_pos += n;
775    }
776    output.truncate(output_pos);
777    {
778        use std::io::Write;
779        let mut file = std::fs::File::create("test").unwrap();
780        file.write_all(&output).unwrap();
781    }
782    println!("input len = {:?}, pos = {:?}", input.len(), output_pos);
783
784    let mut decomp = Vec::new();
785    let mut s = { Seekable::init_buf(&output).unwrap() };
786    for frame in 0..s.get_num_frames() {
787        let size = s.get_frame_decompressed_size(frame);
788        println!("{:?} {:?}", size, s.get_frame_decompressed_offset(frame));
789        let n = decomp.len();
790        decomp.extend(std::iter::repeat(0).take(size));
791        s.decompress_frame(&mut decomp[n..], frame);
792    }
793    println!("{:?}", std::str::from_utf8(&decomp).unwrap());
794    assert_eq!(&input[..], &decomp[..])
795}