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