dsi-bitstream 0.10.1

A Rust implementation of read/write bit streams supporting several types of instantaneous codes
Documentation
/*
 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
 * SPDX-FileCopyrightText: 2023 Inria
 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
 *
 * SPDX-License-Identifier: Apache-2.0 OR MIT
 */

use core::error::Error;

use num_primitive::PrimitiveUnsigned;

/// This is a convenience trait bundling the bounds required for words read and
/// written by either a [`WordRead`] or [`WordWrite`], respectively.
///
/// We provide `ZERO` and `ONE` associated constants to avoid depending on
/// [`num-traits`]'s [`ConstZero`] and [`ConstOne`].
///
/// [`num-traits`]: https://crates.io/crates/num-traits
/// [`ConstZero`]: https://docs.rs/num-traits/latest/num_traits/identities/trait.ConstZero.html
/// [`ConstOne`]: https://docs.rs/num-traits/latest/num_traits/identities/trait.ConstOne.html
pub trait Word: PrimitiveUnsigned {
    const ZERO: Self;
    const ONE: Self;
}

macro_rules! impl_word {
    ($($t:ty),*) => {
        $(
            impl Word for $t {
                const ZERO: Self = 0;
                const ONE: Self = 1;
            }
        )*
    };
}

impl_word!(u8, u16, u32, u64, u128, usize);

/// Trait providing the double-width type for a given unsigned integer type.
///
/// This is used by [`crate::impls::BufBitReader`] to provide a bit buffer
/// that is twice the width of the word read from the backend.
///
/// The methods [`as_double`]/[`as_u64`] can be used to convert a word into its
/// double-width type or to a `u64`, respectively, without loss of precision.
///
/// [`as_double`]: Self::as_double
/// [`as_u64`]: Self::as_u64
pub trait DoubleType {
    type DoubleType: Word;

    /// Converts a word into its double-width type without loss of precision.
    fn as_double(&self) -> Self::DoubleType;

    /// Converts a word into a `u64` without loss of precision.
    fn as_u64(&self) -> u64;
}

macro_rules! impl_double_type {
    ($($t:ty => $d:ty),*) => {
        $(
            impl DoubleType for $t {
                type DoubleType = $d;

                fn as_double(&self) -> Self::DoubleType {
                    *self as Self::DoubleType
                }

                fn as_u64(&self) -> u64 {
                    *self as u64
                }
            }
        )*
    };
}

impl_double_type!(
    u8 => u16,
    u16 => u32,
    u32 => u64,
    u64 => u128
);

/// Sequential, streaming word-by-word reads.
pub trait WordRead {
    type Error: Error + Send + Sync + 'static;

    /// The word type (the type of the result of [`WordRead::read_word`]).
    type Word: Word;

    /// Reads a word and advances the current position.
    fn read_word(&mut self) -> Result<Self::Word, Self::Error>;

    /// Reads and consumes the next word if the backend can determine cheaply
    /// that one is available, returning `None` otherwise (in particular, at
    /// the end of the stream, or when availability cannot be determined
    /// without blocking or performing I/O).
    ///
    /// `None` leaves the current position unchanged, and this method never
    /// fails: read-and-consume is a single atomic operation, so callers can
    /// combine it with buffered state without an error path in between (a
    /// separate peek-then-skip pair would allow the skip to fail after the
    /// caller committed to consuming the word).
    ///
    /// The default implementation returns `None`. Backends with cheap random
    /// access (e.g., [`MemWordReader`]) should override this method: readers
    /// such as [`BufBitReader`] use it to provide branch-free read paths,
    /// falling back to [`read_word`] when `None` is returned.
    ///
    /// [`MemWordReader`]: crate::impls::MemWordReader
    /// [`BufBitReader`]: crate::impls::BufBitReader
    /// [`read_word`]: WordRead::read_word
    #[inline(always)]
    fn read_word_opt(&mut self) -> Option<Self::Word> {
        None
    }
}

/// Sequential, streaming word-by-word writes.
pub trait WordWrite {
    type Error: Error + Send + Sync + 'static;

    /// The word type (the type of the argument of [`WordWrite::write_word`]).
    type Word: Word;

    /// Writes a word and advances the current position.
    fn write_word(&mut self, word: Self::Word) -> Result<(), Self::Error>;

    /// Flushes the stream.
    fn flush(&mut self) -> Result<(), Self::Error>;
}

/// Seekability for [`WordRead`] and [`WordWrite`] streams.
pub trait WordSeek {
    type Error: Error + Send + Sync + 'static;
    /// Gets the current position in words from the start of the stream.
    ///
    /// Note that, consistently with [`Seek::stream_position`], this method
    /// takes a mutable reference to `self`.
    ///
    /// [`Seek::stream_position`]: https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position
    fn word_pos(&mut self) -> Result<u64, Self::Error>;

    /// Sets the current position in words from the start of the stream to `word_pos`.
    fn set_word_pos(&mut self, word_pos: u64) -> Result<(), Self::Error>;
}

/// Replacement of [`std::io::Error`] for `no_std` environments.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum WordError {
    UnexpectedEof { word_pos: usize },
}

impl core::error::Error for WordError {}
impl core::fmt::Display for WordError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            WordError::UnexpectedEof { word_pos } => {
                write!(f, "unexpected end of data at word position {}", word_pos)
            }
        }
    }
}