Skip to main content

dsi_bitstream/traits/
words.rs

1/*
2 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
3 * SPDX-FileCopyrightText: 2023 Inria
4 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
5 *
6 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
7 */
8
9use core::error::Error;
10
11use num_primitive::PrimitiveUnsigned;
12
13/// This is a convenience trait bundling the bounds required for words read and
14/// written by either a [`WordRead`] or [`WordWrite`], respectively.
15///
16/// We provide `ZERO` and `ONE` associated constants to avoid depending on
17/// [`num-traits`]'s [`ConstZero`] and [`ConstOne`].
18///
19/// [`num-traits`]: https://crates.io/crates/num-traits
20/// [`ConstZero`]: https://docs.rs/num-traits/latest/num_traits/identities/trait.ConstZero.html
21/// [`ConstOne`]: https://docs.rs/num-traits/latest/num_traits/identities/trait.ConstOne.html
22pub trait Word: PrimitiveUnsigned {
23    const ZERO: Self;
24    const ONE: Self;
25}
26
27macro_rules! impl_word {
28    ($($t:ty),*) => {
29        $(
30            impl Word for $t {
31                const ZERO: Self = 0;
32                const ONE: Self = 1;
33            }
34        )*
35    };
36}
37
38impl_word!(u8, u16, u32, u64, u128, usize);
39
40/// Trait providing the double-width type for a given unsigned integer type.
41///
42/// This is used by [`crate::impls::BufBitReader`] to provide a bit buffer
43/// that is twice the width of the word read from the backend.
44///
45/// The methods [`as_double`]/[`as_u64`] can be used to convert a word into its
46/// double-width type or to a `u64`, respectively, without loss of precision.
47///
48/// [`as_double`]: Self::as_double
49/// [`as_u64`]: Self::as_u64
50pub trait DoubleType {
51    type DoubleType: Word;
52
53    /// Converts a word into its double-width type without loss of precision.
54    fn as_double(&self) -> Self::DoubleType;
55
56    /// Converts a word into a `u64` without loss of precision.
57    fn as_u64(&self) -> u64;
58}
59
60macro_rules! impl_double_type {
61    ($($t:ty => $d:ty),*) => {
62        $(
63            impl DoubleType for $t {
64                type DoubleType = $d;
65
66                fn as_double(&self) -> Self::DoubleType {
67                    *self as Self::DoubleType
68                }
69
70                fn as_u64(&self) -> u64 {
71                    *self as u64
72                }
73            }
74        )*
75    };
76}
77
78impl_double_type!(
79    u8 => u16,
80    u16 => u32,
81    u32 => u64,
82    u64 => u128
83);
84
85/// Sequential, streaming word-by-word reads.
86pub trait WordRead {
87    type Error: Error + Send + Sync + 'static;
88
89    /// The word type (the type of the result of [`WordRead::read_word`]).
90    type Word: Word;
91
92    /// Reads a word and advances the current position.
93    fn read_word(&mut self) -> Result<Self::Word, Self::Error>;
94
95    /// Reads and consumes the next word if the backend can determine cheaply
96    /// that one is available, returning `None` otherwise (in particular, at
97    /// the end of the stream, or when availability cannot be determined
98    /// without blocking or performing I/O).
99    ///
100    /// `None` leaves the current position unchanged, and this method never
101    /// fails: read-and-consume is a single atomic operation, so callers can
102    /// combine it with buffered state without an error path in between (a
103    /// separate peek-then-skip pair would allow the skip to fail after the
104    /// caller committed to consuming the word).
105    ///
106    /// The default implementation returns `None`. Backends with cheap random
107    /// access (e.g., [`MemWordReader`]) should override this method: readers
108    /// such as [`BufBitReader`] use it to provide branch-free read paths,
109    /// falling back to [`read_word`] when `None` is returned.
110    ///
111    /// [`MemWordReader`]: crate::impls::MemWordReader
112    /// [`BufBitReader`]: crate::impls::BufBitReader
113    /// [`read_word`]: WordRead::read_word
114    #[inline(always)]
115    fn read_word_opt(&mut self) -> Option<Self::Word> {
116        None
117    }
118}
119
120/// Sequential, streaming word-by-word writes.
121pub trait WordWrite {
122    type Error: Error + Send + Sync + 'static;
123
124    /// The word type (the type of the argument of [`WordWrite::write_word`]).
125    type Word: Word;
126
127    /// Writes a word and advances the current position.
128    fn write_word(&mut self, word: Self::Word) -> Result<(), Self::Error>;
129
130    /// Flushes the stream.
131    fn flush(&mut self) -> Result<(), Self::Error>;
132}
133
134/// Seekability for [`WordRead`] and [`WordWrite`] streams.
135pub trait WordSeek {
136    type Error: Error + Send + Sync + 'static;
137    /// Gets the current position in words from the start of the stream.
138    ///
139    /// Note that, consistently with [`Seek::stream_position`], this method
140    /// takes a mutable reference to `self`.
141    ///
142    /// [`Seek::stream_position`]: https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position
143    fn word_pos(&mut self) -> Result<u64, Self::Error>;
144
145    /// Sets the current position in words from the start of the stream to `word_pos`.
146    fn set_word_pos(&mut self, word_pos: u64) -> Result<(), Self::Error>;
147}
148
149/// Replacement of [`std::io::Error`] for `no_std` environments.
150#[derive(Debug, Clone, PartialEq, Eq, Hash)]
151pub enum WordError {
152    UnexpectedEof { word_pos: usize },
153}
154
155impl core::error::Error for WordError {}
156impl core::fmt::Display for WordError {
157    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
158        match self {
159            WordError::UnexpectedEof { word_pos } => {
160                write!(f, "unexpected end of data at word position {}", word_pos)
161            }
162        }
163    }
164}