Skip to main content

dsi_bitstream/codes/
vbyte.rs

1/*
2 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
3 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR MIT
6 */
7
8//! Variable-length byte codes.
9//!
10//! These codes represent a natural number as a sequence of bytes, the length of
11//! the sequence depends on the magnitude of the number. They are used in many
12//! contexts, and they are known under a plethora of different names such as
13//! “vbyte”, “varint”, “[variable-length quantity]”, “LEB”, and so on.
14//!
15//! There are several variants of their definition, but their implied
16//! distribution is always ≈ 1/*x*<sup>8/7</sup>
17//!
18//! The supported range is [0 . . 2⁶⁴).
19//!
20//! # Definition
21//!
22//! Since there are a few slightly different variants used in production code
23//! and in the literature, before going into the details of this implementation
24//! we will try to define a clear taxonomy by explaining in detail the
25//! three properties that define such variants.
26//!
27//! The simplest variable-length byte code encodes a number with a binary
28//! representation of *k* bits using ⌈*k* / 7⌉ bytes. The binary representation
29//! is left-padded with zeros so to obtain exactly ⌈*k* / 7⌉ blocks of 7 bits.
30//! Each block is stored in a byte, prefixed with a continuation bit which is
31//! one for all blocks except for the last one.
32//!
33//! ## Endianness
34//!
35//! The first property is the endianness of the bytes: in big-endian codes, the
36//! first byte contains the highest (most significant) bits, whereas in
37//! little-endian codes, the first byte contains the lowest (least significant)
38//! bits.
39//!
40//! In the big-endian variant the first byte holds the most significant bits.
41//! Note that the *complete* codes implemented here (which drop the redundant
42//! encodings) are not lexicographic across code-length boundaries: for example
43//! 16511 encodes to `[0xff, 0x7f]`, but the larger 16512 encodes to
44//! `[0x80, 0x80, 0x00]`, which compares byte-wise smaller.
45//!
46//! ## Completeness
47//!
48//! This basic representation discussed above is not *complete*, as there are
49//! sequences that are not used. For example, zero can be written in many ways
50//! (e.g., `0x00` or `0x80 0x00`), but we are using only the single-byte
51//! representation. Incompleteness leads to a (small) loss in compression.
52//!
53//! To have completeness, one can offset the representation in *k* bits by the
54//! maximum number representable using *k* − 1 bits. That is, we represent the
55//! interval [0 . . 2⁷) with one byte, then the interval [2⁷ . . 2⁷ + 2¹⁴) with two
56//! bytes, the interval [2⁷ + 2¹⁴ . . 2⁷ + 2¹⁴ + 2²¹) with three bytes, and so on.
57//!
58//! ## Grouping
59//!
60//! In the basic representation, the continuation bit is the most significant
61//! bit of each byte. However, one can gather all continuation bits in the first
62//! byte ([as UTF-8 does]). This approach makes it possible to compute the
63//! length of the code using a call to [`u8::leading_ones`] on the first negated
64//! byte, which usually maps to a negation and a call to a fast instruction for
65//! the detection of the most significant bit, improving branch prediction.
66//!
67//! Note that if the code is grouped, choosing a code with the same endianness
68//! as your hardware can lead to a performance improvement, as after the first
69//! byte the rest of the code can be read with a [`read_exact`].
70//!
71//! ## Sign
72//!
73//! It is possible to extend the codes to represent signed integers. Two
74//! possible approaches are using a [bijective mapping] between the integers and
75//! the natural numbers, or defining a specialized code.
76//!
77//! # Implementations
78//!
79//! We provide two unsigned, ungrouped, complete representations, one
80//! [big-endian] and one [little-endian]. We recommend in general the big-endian
81//! version as it is lexicographical. However, the big-endian version needs a
82//! small buffer when writing, so on some hardware the little-endian version
83//! might be faster.
84//!
85//! Note that the endianness of the code is independent from the endianness of
86//! the underlying bit stream, as it just depends on the order in which bytes are
87//! written.
88//!
89//! Since this code is byte-aligned, we provide also convenient, fast methods
90//! [`vbyte_write`] and [`vbyte_read`] that can be used on types implementing
91//! [`std::io::Read`] and [`std::io::Write`]. There are also endianness-specific
92//! methods [`vbyte_write_be`], [`vbyte_write_le`], [`vbyte_read_be`], and
93//! [`vbyte_read_le`].
94//!
95//! # Examples
96//!
97//! - The [LEB128] code used by LLVM is a little-endian incomplete ungrouped
98//!   representation. There is both a signed and an unsigned version; the signed
99//!   version represents negative numbers using two's complement.
100//!
101//! - The [code used by git] is a big-endian complete ungrouped representation.
102//!
103//! - [This implementation in folly] is a little-endian incomplete ungrouped
104//!   representation.
105//!
106//! [variable-length quantity]: https://en.wikipedia.org/wiki/Variable-length_quantity
107//! [as UTF-8 does]: https://en.wikipedia.org/wiki/UTF-8
108//! [`read_exact`]: std::io::Read::read_exact
109//! [bijective mapping]: crate::codes::ToInt
110//! [big-endian]: VByteBeRead
111//! [little-endian]: VByteLeRead
112//! [LEB128]: https://en.wikipedia.org/wiki/LEB128
113//! [code used by git]: https://github.com/git/git/blob/7fb6aefd2aaffe66e614f7f7b83e5b7ab16d4806/varint.c
114//! [This implementation in folly]: https://github.com/facebook/folly/blob/dd4a5eb057afbc3c7c7da71801df2ee3c61c47d1/folly/Varint.h#L109
115
116use crate::traits::*;
117
118/// Returns the length of the variable-length byte code for `n` in bytes.
119#[must_use]
120#[inline(always)]
121pub const fn byte_len_vbyte(mut n: u64) -> usize {
122    let mut len = 1;
123    loop {
124        n >>= 7;
125        if n == 0 {
126            return len;
127        }
128        n -= 1;
129        len += 1;
130    }
131}
132
133/// Returns the length of the variable-length byte code for `n` in bits.
134#[must_use]
135#[inline(always)]
136pub const fn bit_len_vbyte(n: u64) -> usize {
137    8 * byte_len_vbyte(n)
138}
139/// The number of bytes of the longest vbyte code for a `u64`; no
140/// well-formed code is longer, and no well-formed code of this length
141/// decodes to a value above [`u64::MAX`]. Used by the debug-only
142/// malformed-input guards in the decoders.
143const MAX_VBYTE_LEN: usize = byte_len_vbyte(u64::MAX);
144
145/// Trait for reading big-endian variable-length byte codes.
146///
147/// Note that the endianness of the code is independent
148/// from the endianness of the underlying bit stream.
149pub trait VByteBeRead<E: Endianness>: BitRead<E> {
150    fn read_vbyte_be(&mut self) -> Result<u64, Self::Error>;
151}
152
153/// Trait for reading little-endian variable-length byte codes.
154///
155/// Note that the endianness of the code is independent
156/// from the endianness of the underlying bit stream.
157pub trait VByteLeRead<E: Endianness>: BitRead<E> {
158    fn read_vbyte_le(&mut self) -> Result<u64, Self::Error>;
159}
160
161impl<E: Endianness, B: BitRead<E>> VByteBeRead<E> for B {
162    #[inline(always)]
163    fn read_vbyte_be(&mut self) -> Result<u64, Self::Error> {
164        let mut byte = self.read_bits(8)?;
165        let mut value = byte & 0x7F;
166        // Malformed-input guards, compiled out of release builds: a vbyte
167        // code for a u64 has at most MAX_VBYTE_LEN bytes and its value must
168        // fit in a u64. The value is mirrored in u128 shadow arithmetic
169        // because the u64 shift below drops overflowing bits silently.
170        #[cfg(debug_assertions)]
171        let (mut bytes, mut shadow) = (1_usize, u128::from(byte & 0x7F));
172        while (byte >> 7) != 0 {
173            value += 1;
174            byte = self.read_bits(8)?;
175            #[cfg(debug_assertions)]
176            {
177                bytes += 1;
178                debug_assert!(
179                    bytes <= MAX_VBYTE_LEN,
180                    "malformed vbyte code: more than {} bytes",
181                    MAX_VBYTE_LEN
182                );
183                shadow = ((shadow + 1) << 7) | u128::from(byte & 0x7F);
184                debug_assert!(
185                    shadow <= u128::from(u64::MAX),
186                    "malformed vbyte code: the value does not fit in a u64"
187                );
188            }
189            value = (value << 7) | (byte & 0x7F);
190        }
191        Ok(value)
192    }
193}
194
195impl<E: Endianness, B: BitRead<E>> VByteLeRead<E> for B {
196    #[inline(always)]
197    fn read_vbyte_le(&mut self) -> Result<u64, Self::Error> {
198        let mut result = 0;
199        let mut shift = 0;
200        // Malformed-input guards, compiled out of release builds; see
201        // read_vbyte_be. The byte-count assertion also fires before the
202        // shift amount can reach 64.
203        #[cfg(debug_assertions)]
204        let (mut bytes, mut shadow) = (0_usize, 0_u128);
205        loop {
206            let byte = self.read_bits(8)?;
207            #[cfg(debug_assertions)]
208            {
209                bytes += 1;
210                debug_assert!(
211                    bytes <= MAX_VBYTE_LEN,
212                    "malformed vbyte code: more than {} bytes",
213                    MAX_VBYTE_LEN
214                );
215                shadow += u128::from(byte & 0x7F) << shift;
216                if (byte >> 7) != 0 {
217                    shadow += 1_u128 << (shift + 7);
218                }
219                debug_assert!(
220                    shadow <= u128::from(u64::MAX),
221                    "malformed vbyte code: the value does not fit in a u64"
222                );
223            }
224            result += (byte & 0x7F) << shift;
225            if (byte >> 7) == 0 {
226                break;
227            }
228            shift += 7;
229            result += 1 << shift;
230        }
231        Ok(result)
232    }
233}
234
235/// Trait for writing big-endian variable-length byte codes.
236///
237/// Note that the endianness of the code is independent
238/// from the endianness of the underlying bit stream.
239pub trait VByteBeWrite<E: Endianness>: BitWrite<E> {
240    fn write_vbyte_be(&mut self, n: u64) -> Result<usize, Self::Error>;
241}
242/// Trait for writing little-endian variable-length byte codes.
243///
244/// Note that the endianness of the code is independent
245/// from the endianness of the underlying bit stream.
246pub trait VByteLeWrite<E: Endianness>: BitWrite<E> {
247    fn write_vbyte_le(&mut self, n: u64) -> Result<usize, Self::Error>;
248}
249
250impl<E: Endianness, B: BitWrite<E>> VByteBeWrite<E> for B {
251    #[inline(always)]
252    fn write_vbyte_be(&mut self, mut n: u64) -> Result<usize, Self::Error> {
253        let mut buf = [0u8; 10];
254        let mut pos = buf.len() - 1;
255        buf[pos] = (n & 0x7F) as u8;
256        n >>= 7;
257        while n != 0 {
258            n -= 1;
259            pos -= 1;
260            buf[pos] = 0x80 | (n & 0x7F) as u8;
261            n >>= 7;
262        }
263        let bytes_to_write = buf.len() - pos;
264        for &byte in &buf[pos..] {
265            self.write_bits(byte as u64, 8)?;
266        }
267        Ok(bytes_to_write * 8)
268    }
269}
270
271impl<E: Endianness, B: BitWrite<E>> VByteLeWrite<E> for B {
272    #[inline(always)]
273    fn write_vbyte_le(&mut self, mut n: u64) -> Result<usize, Self::Error> {
274        let mut len = 1;
275        loop {
276            let byte = (n & 0x7F) as u8;
277            n >>= 7;
278            if n != 0 {
279                self.write_bits((byte | 0x80) as u64, 8)?;
280            } else {
281                self.write_bits(byte as u64, 8)?;
282                break;
283            }
284            n -= 1;
285            len += 1;
286        }
287        Ok(len * 8)
288    }
289}
290
291/// Writes a natural number to a byte stream using variable-length byte codes and
292/// returns the number of bytes written.
293///
294/// This method just delegates to the correct endianness-specific method.
295#[inline(always)]
296#[cfg(feature = "std")]
297pub fn vbyte_write<E: Endianness, W: std::io::Write>(
298    n: u64,
299    writer: &mut W,
300) -> std::io::Result<usize> {
301    if core::any::TypeId::of::<E>() == core::any::TypeId::of::<BigEndian>() {
302        vbyte_write_be(n, writer)
303    } else {
304        vbyte_write_le(n, writer)
305    }
306}
307
308/// Encode a natural number to a big-endian byte stream using variable-length
309/// byte codes and return the number of bytes written.
310#[inline(always)]
311#[cfg(feature = "std")]
312pub fn vbyte_write_be(mut n: u64, w: &mut impl std::io::Write) -> std::io::Result<usize> {
313    let mut buf = [0u8; 10];
314    let mut pos = buf.len() - 1;
315    buf[pos] = (n & 0x7F) as u8;
316    n >>= 7;
317    while n != 0 {
318        n -= 1;
319        pos -= 1;
320        buf[pos] = 0x80 | (n & 0x7F) as u8;
321        n >>= 7;
322    }
323    let bytes_to_write = buf.len() - pos;
324    w.write_all(&buf[pos..])?;
325    Ok(bytes_to_write)
326}
327
328/// Encode a natural number to a little-endian byte stream using variable-length
329/// byte codes and return the number of bytes written.
330#[inline(always)]
331#[cfg(feature = "std")]
332pub fn vbyte_write_le(mut n: u64, writer: &mut impl std::io::Write) -> std::io::Result<usize> {
333    let mut len = 1;
334    loop {
335        let byte = (n & 0x7F) as u8;
336        n >>= 7;
337        if n != 0 {
338            writer.write_all(&[byte | 0x80])?;
339        } else {
340            writer.write_all(&[byte])?;
341            break;
342        }
343        n -= 1;
344        len += 1;
345    }
346    Ok(len)
347}
348
349/// Decode a natural number from a byte stream using variable-length byte codes.
350///
351/// This method just delegates to the correct endianness-specific method.
352#[inline(always)]
353#[cfg(feature = "std")]
354pub fn vbyte_read<E: Endianness, R: std::io::Read>(reader: &mut R) -> std::io::Result<u64> {
355    if core::any::TypeId::of::<E>() == core::any::TypeId::of::<BigEndian>() {
356        vbyte_read_be(reader)
357    } else {
358        vbyte_read_le(reader)
359    }
360}
361
362/// Decode a natural number from a big-endian byte stream using variable-length
363/// byte codes.
364#[inline(always)]
365#[cfg(feature = "std")]
366pub fn vbyte_read_be(reader: &mut impl std::io::Read) -> std::io::Result<u64> {
367    let mut buf = [0u8; 1];
368    let mut value: u64;
369    reader.read_exact(&mut buf)?;
370    value = (buf[0] & 0x7F) as u64;
371    // Malformed-input guards, compiled out of release builds; see
372    // read_vbyte_be in VByteBeRead.
373    #[cfg(debug_assertions)]
374    let (mut bytes, mut shadow) = (1_usize, u128::from(buf[0] & 0x7F));
375    while (buf[0] >> 7) != 0 {
376        value += 1;
377        reader.read_exact(&mut buf)?;
378        #[cfg(debug_assertions)]
379        {
380            bytes += 1;
381            debug_assert!(
382                bytes <= MAX_VBYTE_LEN,
383                "malformed vbyte code: more than {} bytes",
384                MAX_VBYTE_LEN
385            );
386            shadow = ((shadow + 1) << 7) | u128::from(buf[0] & 0x7F);
387            debug_assert!(
388                shadow <= u128::from(u64::MAX),
389                "malformed vbyte code: the value does not fit in a u64"
390            );
391        }
392        value = (value << 7) | ((buf[0] & 0x7F) as u64);
393    }
394    Ok(value)
395}
396
397/// Decode a natural number from a little-endian byte stream using
398/// variable-length byte codes.
399#[inline(always)]
400#[cfg(feature = "std")]
401pub fn vbyte_read_le(reader: &mut impl std::io::Read) -> std::io::Result<u64> {
402    let mut result = 0;
403    let mut shift = 0;
404    let mut buffer = [0; 1];
405    // Malformed-input guards, compiled out of release builds; see
406    // read_vbyte_le in VByteLeRead.
407    #[cfg(debug_assertions)]
408    let (mut bytes, mut shadow) = (0_usize, 0_u128);
409    loop {
410        reader.read_exact(&mut buffer)?;
411        let byte = buffer[0];
412        #[cfg(debug_assertions)]
413        {
414            bytes += 1;
415            debug_assert!(
416                bytes <= MAX_VBYTE_LEN,
417                "malformed vbyte code: more than {} bytes",
418                MAX_VBYTE_LEN
419            );
420            shadow += u128::from(byte & 0x7F) << shift;
421            if (byte >> 7) != 0 {
422                shadow += 1_u128 << (shift + 7);
423            }
424            debug_assert!(
425                shadow <= u128::from(u64::MAX),
426                "malformed vbyte code: the value does not fit in a u64"
427            );
428        }
429        result += ((byte & 0x7F) as u64) << shift;
430        if (byte >> 7) == 0 {
431            break;
432        }
433        shift += 7;
434        result += 1 << shift;
435    }
436    Ok(result)
437}
438
439#[cfg(test)]
440#[cfg(feature = "std")]
441mod tests {
442    #[allow(unused_imports)]
443    use super::*;
444
445    /// An 11-byte run of continuation bytes: one byte longer than the
446    /// longest well-formed u64 code. Caught by the debug-only byte-count
447    /// guard; release builds keep the unchecked decode paths.
448    #[cfg(debug_assertions)]
449    const OVERLONG: [u8; 12] = [0xFF; 12];
450
451    /// A terminated ten-byte code whose payload exceeds u64::MAX. Caught by
452    /// the debug-only u128 shadow accumulation (the u64 arithmetic would
453    /// truncate silently).
454    #[cfg(debug_assertions)]
455    const HIGH_PAYLOAD: [u8; 10] = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F];
456
457    macro_rules! malformed_tests {
458        ($bit_be:ident, $bit_le:ident, $io_be:ident, $io_le:ident, $data:ident) => {
459            #[test]
460            #[cfg(debug_assertions)]
461            #[should_panic(expected = "malformed vbyte code")]
462            fn $bit_be() {
463                use crate::prelude::{BufBitReader, MemWordReader};
464                let mut r = <BufBitReader<crate::traits::BE, _>>::new(MemWordReader::new(&$data));
465                let _ = r.read_vbyte_be();
466            }
467
468            #[test]
469            #[cfg(debug_assertions)]
470            #[should_panic(expected = "malformed vbyte code")]
471            fn $bit_le() {
472                use crate::prelude::{BufBitReader, MemWordReader};
473                let mut r = <BufBitReader<crate::traits::LE, _>>::new(MemWordReader::new(&$data));
474                let _ = r.read_vbyte_le();
475            }
476
477            #[test]
478            #[cfg(debug_assertions)]
479            #[should_panic(expected = "malformed vbyte code")]
480            fn $io_be() {
481                let _ = vbyte_read_be(&mut std::io::Cursor::new(&$data));
482            }
483
484            #[test]
485            #[cfg(debug_assertions)]
486            #[should_panic(expected = "malformed vbyte code")]
487            fn $io_le() {
488                let _ = vbyte_read_le(&mut std::io::Cursor::new(&$data));
489            }
490        };
491    }
492
493    malformed_tests!(
494        test_overlong_bit_be,
495        test_overlong_bit_le,
496        test_overlong_io_be,
497        test_overlong_io_le,
498        OVERLONG
499    );
500    malformed_tests!(
501        test_high_payload_bit_be,
502        test_high_payload_bit_le,
503        test_high_payload_io_be,
504        test_high_payload_io_le,
505        HIGH_PAYLOAD
506    );
507
508    const UPPER_BOUND_1: u64 = 128;
509    const UPPER_BOUND_2: u64 = 128_u64.pow(2) + UPPER_BOUND_1;
510    const UPPER_BOUND_3: u64 = 128_u64.pow(3) + UPPER_BOUND_2;
511    const UPPER_BOUND_4: u64 = 128_u64.pow(4) + UPPER_BOUND_3;
512    const UPPER_BOUND_5: u64 = 128_u64.pow(5) + UPPER_BOUND_4;
513    const UPPER_BOUND_6: u64 = 128_u64.pow(6) + UPPER_BOUND_5;
514    const UPPER_BOUND_7: u64 = 128_u64.pow(7) + UPPER_BOUND_6;
515    const UPPER_BOUND_8: u64 = 128_u64.pow(8) + UPPER_BOUND_7;
516
517    macro_rules! impl_tests {
518        ($test_name:ident, $E:ty) => {
519            #[test]
520            fn $test_name() -> std::io::Result<()> {
521                const MAX: usize = 1 << 20;
522                const MIN: usize = 0;
523                let mut buffer = std::io::Cursor::new(Vec::with_capacity(128));
524                let mut lens = Vec::new();
525
526                for i in MIN..MAX {
527                    lens.push(vbyte_write::<$E, _>(i as _, &mut buffer)?);
528                }
529                buffer.set_position(0);
530                for (i, l) in (MIN..MAX).zip(lens.iter()) {
531                    let j = vbyte_read::<$E, _>(&mut buffer)?;
532                    assert_eq!(byte_len_vbyte(i as _), *l);
533                    assert_eq!(j, i as u64);
534                }
535
536                let values = [
537                    0,
538                    UPPER_BOUND_1 - 1,
539                    UPPER_BOUND_1 + 1,
540                    UPPER_BOUND_2 - 1,
541                    UPPER_BOUND_2 + 1,
542                    UPPER_BOUND_3 - 1,
543                    UPPER_BOUND_3 + 1,
544                    UPPER_BOUND_4 - 1,
545                    UPPER_BOUND_4 + 1,
546                    UPPER_BOUND_5 - 1,
547                    UPPER_BOUND_5 + 1,
548                    UPPER_BOUND_6 - 1,
549                    UPPER_BOUND_6 + 1,
550                    UPPER_BOUND_7 - 1,
551                    UPPER_BOUND_7 + 1,
552                    UPPER_BOUND_8 - 1,
553                    UPPER_BOUND_8 + 1,
554                    u64::MAX,
555                ];
556
557                let tell: u64 = buffer.position();
558                for &i in values.iter() {
559                    vbyte_write::<$E, _>(i, &mut buffer)?;
560                }
561                buffer.set_position(tell);
562                for &i in values.iter() {
563                    assert_eq!(i, vbyte_read::<$E, _>(&mut buffer)?);
564                }
565                Ok(())
566            }
567        };
568    }
569
570    impl_tests!(test_vbytes_be, BE);
571    impl_tests!(test_vbytes_le, LE);
572}