streamvbyte64/
lib.rs

1/*! # Fast Byte-Aligned Integer Coding
2This crate is a Rust port of [Daniel Lemire's `streamvbyte` library](https://github.com/lemire/streamvbyte).
3It contains multiple implementations of this format aimed at different integer sizes and value distributions.
4
5Each `Coder` implementation produces different formats that are incompatible with one another. Names
6provide the length of each of the 4 possible tags for each value so `Coder1234` encodes each entry
7as 1, 2, 3, or 4 bytes. A scalar implementation is always available at a large speed penalty but
8the implementation will automatically use an accelerated implementation for the target if available.
9
10At the moment group implementations only have acceleration on little-endian `aarch64` targets with
11`NEON` instruction support.
12
13## Example without delta-coding
14
15```
16use streamvbyte64::{Coder, Coder1234};
17
18let coder = Coder1234::new();
19let values = vec![
20    0u32, 128, 256, 1024, 70, 36, 1000000,
21    378, 45, 888, 26, 262144, 88, 89, 90, 16777216
22];
23let (tag_len, data_len) = Coder1234::max_compressed_bytes(values.len());
24let mut encoded = vec![0u8; tag_len + data_len];
25let (tags, data) = encoded.split_at_mut(tag_len);
26// This is the number of bytes in data actually used. If you're writing the encoded data
27// to an output you would write tags and &data[..encoded_data_len].
28let encoded_data_len = coder.encode(&values, tags, data);
29
30let mut decoded = vec![0u32; values.len()];
31coder.decode(tags, &data[..encoded_data_len], &mut decoded);
32assert_eq!(values, decoded);
33
34// You can also use this a bit like an array using data_len() to skip whole groups
35// at the cost of reading all the tags before the target group.
36let mut group = [0u32; 4];
37coder.decode(&tags[2..3], &data[coder.data_len(&tags[..2])..], &mut group);
38assert_eq!(values[11], group[3]);
39```
40
41## Example with delta coding
42
43```
44use streamvbyte64::{Coder, Coder1234};
45
46let coder = Coder1234::new();
47let mut sum = 0u32;
48let values = [
49    0u32, 128, 256, 1024, 70, 36, 1000000,
50    378, 45, 888, 26, 262144, 88, 89, 90, 16777216
51].iter().map(|x| {
52    sum += x;
53    sum
54}).collect::<Vec<_>>();
55let (tag_len, data_len) = Coder1234::max_compressed_bytes(values.len());
56let mut encoded = vec![0u8; tag_len + data_len];
57let (tags, data) = encoded.split_at_mut(tag_len);
58let nodelta_data_len = coder.encode(&values, tags, data);
59// encode_deltas() and decode_deltas() both accept an initial value that is subtracted from
60// or added to all the value in the stream. At the beginning of the stream this is usually
61// zero but may be non-zero if you are encoding/decoding in the middle of a stream.
62let encoded_data_len = coder.encode_deltas(0, &values, tags, data);
63// Fewer bytes are written with the delta encoding as we only record the distance between
64// each value and not the value itself.
65assert!(encoded_data_len < nodelta_data_len);
66
67let mut decoded = vec![0u32; values.len()];
68coder.decode_deltas(0, tags, &data[..encoded_data_len], &mut decoded);
69assert_eq!(values, decoded);
70
71// You can also use this a bit like an array using skip_deltas() to skip whole groups
72// at a cost that is less than straight decoding.
73let (skip_data_len, initial) = coder.skip_deltas(&tags[..2], data);
74let mut group = [0u32; 4];
75coder.decode_deltas(initial, &tags[2..3], &data[skip_data_len..], &mut group);
76assert_eq!(values[11], group[3]);
77```
78*/
79
80mod arch;
81mod coder_impl;
82mod coding_descriptor;
83mod raw_group;
84mod tag_utils;
85
86mod coder0124;
87mod coder1234;
88mod coder1248;
89
90pub use num_traits::{ops::wrapping::WrappingAdd, ops::wrapping::WrappingSub, PrimInt};
91
92/// `Coder` compresses and decompresses integers in a byte-aligned format compose of two streams.
93///
94/// Groups of 4 integers are coded into two separate streams: a tag stream where each byte describes
95/// a group, and a data stream containing values as described by the tag. The coder _does not_
96/// record the number of entries in the stream, instead assuming a multiple of 4.
97///
98/// Different coder implementations support different integer widths (32 or 64 bit) as well as
99/// different byte length distributions to better compress some data sets.
100///
101/// Use `max_compressed_bytes()` to compute the number of tag and data bytes that must be allocated
102/// to safely encode a slice of input values.
103pub trait Coder: Sized + Copy + Clone {
104    /// The input/output element type for this coder, typically `u32` or `u64`.
105    type Elem: PrimInt + WrappingAdd + WrappingSub + std::fmt::Debug + Sized + Copy + Clone;
106
107    /// Create a new `Coder`, selecting the fastest implementation available.
108    ///
109    /// These objects should be relatively cheap to create and require no heap allocation.
110    fn new() -> Self;
111
112    /// Returns the number of `(tag_bytes, data_bytes)` required to compress a slice of length `len`.
113    fn max_compressed_bytes(len: usize) -> (usize, usize) {
114        let num_groups = (len + 3) / 4;
115        (
116            num_groups,
117            num_groups * 4 * std::mem::size_of::<Self::Elem>(),
118        )
119    }
120
121    /// Encodes a slice of values, writing tags and data to separate streams.
122    ///
123    /// For every 4 input values one tag byte and up to `std::mem::size_of::<Elem>() * 4` data bytes
124    /// may be written to output.
125    ///
126    /// Returns the number of bytes written to the data stream.
127    ///
128    /// # Panics
129    ///
130    /// - If `values.len() % 4 != 0`
131    /// - If `tags` or `data` are too small to fit all of the output data.
132    fn encode(&self, values: &[Self::Elem], tags: &mut [u8], data: &mut [u8]) -> usize;
133
134    /// Encodes a slice of values, writing tags and data to separate streams.
135    ///
136    /// Values are interpreted as deltas starting from `initial` which produces a more compact
137    /// output that is also more expensive to encode and decode.
138    ///
139    /// For every 4 input values one tag byte and up to `std::mem::size_of::<Elem>() * 4` data bytes
140    /// may be written to output.
141    ///
142    /// Returns the number of bytes written to the data stream.
143    ///
144    /// # Panics
145    ///
146    /// - If `values.len() % 4 != 0`
147    /// - If `tags` or `data` are too small to fit all of the output data.
148    fn encode_deltas(
149        &self,
150        initial: Self::Elem,
151        values: &[Self::Elem],
152        tags: &mut [u8],
153        data: &mut [u8],
154    ) -> usize;
155
156    /// Decodes input tags and data streams to an output slice.
157    ///
158    /// Consumes all tag values in the input stream to produce `tags.len() * 4` values. May consume
159    /// up to `std::mem::size_of<Elem>() * tags.len() * 4` bytes from data.
160    ///
161    /// Returns the number of bytes consumed from the data stream.
162    ///
163    /// # Panics
164    ///
165    /// - If `values.len() % 4 != 0`.
166    /// - If `tags.len() < values.len() / 4`.
167    /// - If decoding would consume bytes past the end of `data`.
168    fn decode(&self, tags: &[u8], data: &[u8], values: &mut [Self::Elem]) -> usize;
169
170    /// Decodes input tags and data streams to an output slice.
171    ///
172    /// Values are interepreted as deltas starting from `initial`.
173    ///
174    /// Consumes all tag values in the input stream to produce `tags.len() * 4` values. May consume
175    /// up to `std::mem::size_of<Elem>() * tags.len() * 4` bytes from data.
176    ///
177    /// Returns the number of bytes consumed from the data stream.
178    ///
179    /// # Panics
180    ///
181    /// - If `values.len() % 4 != 0`.
182    /// - If `tags.len() < values.len() / 4`.
183    /// - If decoding would consume bytes past the end of `data`.
184    fn decode_deltas(
185        &self,
186        initial: Self::Elem,
187        tags: &[u8],
188        data: &[u8],
189        values: &mut [Self::Elem],
190    ) -> usize;
191
192    /// Returns the data length of all the groups encoded by `tags`.
193    fn data_len(&self, tags: &[u8]) -> usize;
194
195    /// Skip `tags.len() * 4` deltas read from input tag and data streams.
196    ///
197    /// Returns the number of bytes consumed from the data stream and the sum of all the deltas that
198    /// were skipped.
199    ///
200    /// # Panics
201    ///
202    ///  - If decoding would consume bytes past the end of `data`.
203    fn skip_deltas(&self, tags: &[u8], data: &[u8]) -> (usize, Self::Elem);
204}
205
206pub use coder0124::Coder0124;
207pub use coder1234::Coder1234;
208pub use coder1248::Coder1248;
209
210#[cfg(test)]
211pub(crate) mod tests;