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}