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 MIT
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}