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`](https://crates.io/crates/num-traits)'s
18/// [`ConstZero`](https://docs.rs/num-traits/latest/num_traits/identities/trait.ConstZero.html)
19/// and
20/// [`ConstOne`](https://docs.rs/num-traits/latest/num_traits/identities/trait.ConstOne.html).
21pub trait Word: PrimitiveUnsigned {
22    const ZERO: Self;
23    const ONE: Self;
24}
25
26macro_rules! impl_word {
27    ($($t:ty),*) => {
28        $(
29            impl Word for $t {
30                const ZERO: Self = 0;
31                const ONE: Self = 1;
32            }
33        )*
34    };
35}
36
37impl_word!(u8, u16, u32, u64, u128, usize);
38
39/// Trait providing the double-width type for a given unsigned integer type.
40///
41/// This is used by [`crate::impls::BufBitReader`] to provide a bit buffer
42/// that is twice the width of the word read from the backend.
43///
44/// The methods
45/// [`as_double`](Self::as_double)/[`as_u64`](Self::as_u64) can be
46/// used to convert a word into its double-width type or to a `u64`,
47/// respectively, without loss of precision.
48pub trait DoubleType {
49    type DoubleType: Word;
50
51    /// Converts a word into its double-width type without loss of precision.
52    fn as_double(&self) -> Self::DoubleType;
53
54    /// Converts a word into a `u64` without loss of precision.
55    fn as_u64(&self) -> u64;
56}
57
58macro_rules! impl_double_type {
59    ($($t:ty => $d:ty),*) => {
60        $(
61            impl DoubleType for $t {
62                type DoubleType = $d;
63
64                fn as_double(&self) -> Self::DoubleType {
65                    *self as Self::DoubleType
66                }
67
68                fn as_u64(&self) -> u64 {
69                    *self as u64
70                }
71            }
72        )*
73    };
74}
75
76impl_double_type!(
77    u8 => u16,
78    u16 => u32,
79    u32 => u64,
80    u64 => u128
81);
82
83/// Sequential, streaming word-by-word reads.
84pub trait WordRead {
85    type Error: Error + Send + Sync + 'static;
86
87    /// The word type (the type of the result of [`WordRead::read_word`]).
88    type Word: Word;
89
90    /// Reads a word and advances the current position.
91    fn read_word(&mut self) -> Result<Self::Word, Self::Error>;
92}
93
94/// Sequential, streaming word-by-word writes.
95pub trait WordWrite {
96    type Error: Error + Send + Sync + 'static;
97
98    /// The word type (the type of the argument of [`WordWrite::write_word`]).
99    type Word: Word;
100
101    /// Writes a word and advances the current position.
102    fn write_word(&mut self, word: Self::Word) -> Result<(), Self::Error>;
103
104    /// Flushes the stream.
105    fn flush(&mut self) -> Result<(), Self::Error>;
106}
107
108/// Seekability for [`WordRead`] and [`WordWrite`] streams.
109pub trait WordSeek {
110    type Error: Error + Send + Sync + 'static;
111    /// Gets the current position in words from the start of the stream.
112    ///
113    /// Note that, consistently with
114    /// [`Seek::stream_position`](https://doc.rust-lang.org/std/io/trait.Seek.html#method.stream_position),
115    /// this method takes a mutable reference to `self`.
116    fn word_pos(&mut self) -> Result<u64, Self::Error>;
117
118    /// Sets the current position in words from the start of the stream to `word_pos`.
119    fn set_word_pos(&mut self, word_pos: u64) -> Result<(), Self::Error>;
120}
121
122/// Replacement of [`std::io::Error`] for `no_std` environments.
123#[derive(Debug, Clone, PartialEq, Eq, Hash)]
124pub enum WordError {
125    UnexpectedEof { word_pos: usize },
126}
127
128impl core::error::Error for WordError {}
129impl core::fmt::Display for WordError {
130    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
131        match self {
132            WordError::UnexpectedEof { word_pos } => {
133                write!(f, "unexpected end of data at word position {}", word_pos)
134            }
135        }
136    }
137}