zstd_framed/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
//! Seekable reader types and writer types for use with zstd-compressed
//! streams made up of multiple frames, especially those that use the [zstd seekable format].
//! Works with sync I/O, and async I/O via the `tokio` and `futures` features.
//!
//! By compressing a file into multiple frames, we can handle seeking
//! relatively efficiently: we first jump to the start of the last frame
//! before the target seek position, then decompress until we reach the
//! target. As long as the file was created with a reasonable frame size,
//! this can be more efficient than decompressing the entire file.
//!
//! This crate uses the [`zstd`] crate for zstd compression and decompression,
//! but includes a custom Rust implementation for the [zstd seekable format]
//! parser and serializer.
//!
//! ## When not to use this crate
//!
//! **This crate exposes a lot of options and can be easily mis-used if
//! you don't review your configuration carefully!** This is not meant to
//! be a general-purpose tool for compression and decompression, but one
//! that gives a lot of flexibility, such as when dealing with
//! partially-written files, or when you need precise control on how a file
//! gets broken up into multiple frames.
//!
//! For more general use, consider one of these alternative crates:
//!
//! - [`zstd`](https://crates.io/crates/zstd) for zstd compression and decompression (with synchronous I/O)
//! - [`async-compression`](https://crates.io/crates/async-compression) for
//! compression and decompression with zstd and many other algorithms (with
//! asynchronous I/O)
//! - [`zstd-seekable`](https://crates.io/crates/zstd-seekable) for compression
//! and decompression using the [zstd seekable format] (with synchronous I/O).
//! It also uses the official upstream zstd implementation for the seekable
//! format.
//!
//! ## Getting started
//!
//! - Sync I/O
//! - Reading a seek table: [`crate::table::read_seek_table`]
//! - Reading a compressed stream: [`crate::ZstdReader`]
//! - Writing a compressed stream [`crate::ZstdWriter`]
//! - Tokio
//! - Reading a seek table: [`crate::table::tokio::read_seek_table`]
//! - Reading a compressed stream: [`crate::AsyncZstdReader`]
//! - Writing a compressed stream: [`crate::AsyncZstdWriter`]
//! - `futures-rs`
//! - Reading a seek table: [`crate::table::futures::read_seek_table`]
//! - Reading a compressed stream: [`crate::AsyncZstdReader`]
//! - Writing a compressed stream: [`crate::AsyncZstdWriter`]
//!
//! ## Examples
//!
//! ```
//! # use std::io::{Read as _, Seek as _, Write as _};
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // -----------------------------
//! // - Write a compressed stream -
//! // -----------------------------
//! let mut compressed_stream = vec![];
//!
//! let mut writer = zstd_framed::ZstdWriter::builder(&mut compressed_stream)
//! .with_compression_level(3) // Set zstd compression level
//! .with_seek_table(1000) // Add a seek table with a frame size of 1000
//! .build()?;
//!
//! // Compress data by writing it to the writer
//! writer.write_all(b"hello world!");
//!
//! // Finish writing the stream
//! writer.shutdown()?;
//! drop(writer);
//!
//! // -------------------------------------
//! // - Read the seek table from a stream -
//! // -------------------------------------
//! let mut compressed_stream = std::io::Cursor::new(compressed_stream);
//! let seek_table = zstd_framed::table::read_seek_table(&mut compressed_stream)?
//! .ok_or("expected stream to contain a seek table")?;
//!
//! // --------------------------------------------------
//! // - Read and decompress a stream with a seek table -
//! // --------------------------------------------------
//! let mut reader = zstd_framed::ZstdReader::builder(&mut compressed_stream)
//! .with_seek_table(seek_table)
//! .build()?;
//!
//! // Seek past the word "hello", then read the rest of the stream
//! let mut decompressed_part = String::new();
//! reader.seek(std::io::SeekFrom::Start(6))?;
//! reader.read_to_string(&mut decompressed_part)?;
//!
//! assert_eq!(decompressed_part, "world!");
//! # Ok(())
//! # }
//! ```
//!
//! ## Feature flags
//!
//! - `tokio`: Enable Tokio support, which enables the [`AsyncZstdReader`]
//! and [`AsyncZstdWriter`] types to be used with the Tokio I/O traits
//! ([`tokio::io::AsyncRead`], [`tokio::io::AsyncWrite`], etc.)
//! - `futures`: Enable support for the `futures-rs` crate, which enables the [`AsyncZstdReader`] and [`AsyncZstdWriter`] types to be used with the `futures-rs` traits ([`futures::AsyncRead`], [`futures::AsyncWrite`], etc.)
//!
//! [zstd seekable format]: https://github.com/facebook/zstd/tree/51eb7daf39c8e8a7c338ba214a9d4e2a6a086826/contrib/seekable_format
pub use async_reader::{AsyncZstdReader, AsyncZstdSeekableReader};
pub use async_writer::AsyncZstdWriter;
pub use reader::ZstdReader;
pub use writer::ZstdWriter;
#[macro_use]
mod macros;
pub mod async_reader;
pub mod async_writer;
mod buffer;
mod decoder;
mod encoder;
pub mod reader;
pub mod table;
pub mod writer;
const SKIPPABLE_HEADER_MAGIC_BYTES: [u8; 4] = 0x184D2A5E_u32.to_le_bytes();
const SEEKABLE_FOOTER_MAGIC_BYTES: [u8; 4] = 0x8F92EAB1_u32.to_le_bytes();
/// Returns the outcome of trying to move some data, such as encoding or
/// decoding a zstd stream.
#[derive(Debug, Clone, Copy)]
enum ZstdOutcome<T> {
/// The operation finished.
Complete(T),
/// The operation did not complete because more data still needs to
/// be moved.
HasMore {
/// A hint on how much more data needs to be moved for the operation
/// to complete.
remaining_bytes: usize,
},
}