dsi_bitstream/impls/
word_adapter.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 crate::traits::*;
10use common_traits::*;
11#[cfg(feature = "mem_dbg")]
12use mem_dbg::{MemDbg, MemSize};
13use std::io::{Read, Seek, SeekFrom, Write};
14
15/// An adapter from [`Read`], [`Write`], and [`Seek`], to [`WordRead`],
16/// [`WordWrite`], and [`WordSeek`], respectively.
17///
18/// Instances of this struct can be created using [`WordAdapter::new`]. They
19/// turn every standard (possibly seekable) source or destination of bytes (such
20/// as [`std::fs::File`], [`std::io::BufReader`], sockets, etc.) into a source
21/// or destination of words.
22///
23/// Due to the necessity of managing files whose length is not a multiple of the
24/// word length, [`read_word`](WordAdapter::read_word) will return a partially
25/// read word extended with zeros at the end of such files.
26///
27/// To provide a sensible value after such a read,
28/// [`word_pos`](WordAdapter::word_pos) will always return the position
29/// of the underlying [`Seek`] rounded up to the next multiple of `W::Bytes`.
30/// This approach, however, requires that if you adapt a [`Seek`], its current position must be
31/// a multiple of `W::Bytes`, or the results of [`word_pos`](WordAdapter::word_pos)
32/// will be shifted by the rounding.
33#[derive(Debug, Clone)]
34#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
35pub struct WordAdapter<W: UnsignedInt + FromBytes + ToBytes, B> {
36    backend: B,
37    _marker: core::marker::PhantomData<W>,
38}
39
40impl<W: UnsignedInt + FromBytes + ToBytes, B> WordAdapter<W, B> {
41    /// Create a new WordAdapter
42    pub fn new(backend: B) -> Self {
43        Self {
44            backend,
45            _marker: core::marker::PhantomData,
46        }
47    }
48
49    pub fn into_inner(self) -> B {
50        self.backend
51    }
52}
53
54impl<W: UnsignedInt + ToBytes + FromBytes + FiniteRangeNumber, B: Read> WordRead
55    for WordAdapter<W, B>
56{
57    type Error = std::io::Error;
58    type Word = W;
59
60    #[inline(always)]
61    fn read_word(&mut self) -> Result<W, Self::Error> {
62        let mut res: W::Bytes = Default::default();
63        self.backend
64            .read_exact(res.as_mut())
65            .map_err(|e|
66                match e.kind() {
67                std::io::ErrorKind::UnexpectedEof => {
68                    std::io::Error::new(
69                        e.kind(),
70                        format!(concat!(
71                            "Unexpected end of file. ",
72                            "This might happen because the file length is not a multiple of the word size ({}). ",
73                            "In this case, please pad with zeros at the end of the file. ",
74                            "The inner std::io::Error was {:?}"), W::BYTES, e),
75                    )
76                }
77                _ => e,
78            })?;
79        Ok(W::from_ne_bytes(res))
80    }
81}
82
83impl<W: UnsignedInt + ToBytes + FromBytes + FiniteRangeNumber, B: Write> WordWrite
84    for WordAdapter<W, B>
85{
86    type Error = std::io::Error;
87    type Word = W;
88
89    #[inline]
90    fn write_word(&mut self, word: W) -> Result<(), std::io::Error> {
91        let _ = self.backend.write(word.to_ne_bytes().as_ref())?;
92        Ok(())
93    }
94
95    fn flush(&mut self) -> Result<(), Self::Error> {
96        self.backend.flush()
97    }
98}
99
100impl<W: UnsignedInt + ToBytes + FromBytes + FiniteRangeNumber, B: Seek> WordSeek
101    for WordAdapter<W, B>
102{
103    type Error = std::io::Error;
104
105    #[inline(always)]
106    fn word_pos(&mut self) -> Result<u64, std::io::Error> {
107        Ok(self.backend.stream_position()?.div_ceil(W::BYTES as u64))
108    }
109
110    #[inline(always)]
111    fn set_word_pos(&mut self, word_index: u64) -> Result<(), std::io::Error> {
112        self.backend
113            .seek(SeekFrom::Start(word_index * W::BYTES as u64))?;
114        Ok(())
115    }
116}
117
118#[cfg(test)]
119mod test {
120    use crate::prelude::*;
121    #[test]
122    fn test_word_adapter() {
123        let data: Vec<u32> = vec![
124            0xa6032421, 0xc9d01b28, 0x168b4ecd, 0xc5ccbed9, 0xfd007100, 0x08469d41, 0x989fd8c2,
125            0x954d351a, 0x3225ec9f, 0xbca253f9, 0x915aad84, 0x274c0de1, 0x4bfc6982, 0x59a47341,
126            0x4e32a33a, 0x9e0d2208,
127        ];
128        let path = std::env::temp_dir().join("test_file_adapter");
129        {
130            let mut writer = <WordAdapter<u32, _>>::new(std::fs::File::create(&path).unwrap());
131            for value in &data {
132                writer.write_word(*value).unwrap();
133            }
134        }
135        {
136            let mut reader = <WordAdapter<u32, _>>::new(std::fs::File::open(&path).unwrap());
137            for value in &data {
138                assert_eq!(*value, reader.read_word().unwrap());
139            }
140        }
141    }
142
143    #[test]
144    fn test_word_adapter_codes() {
145        let data: Vec<u8> = vec![
146            0x5f, 0x68, 0xdb, 0xca, 0x79, 0x17, 0xf3, 0x37, 0x2c, 0x46, 0x63, 0xf7, 0xf3, 0x28,
147            0xa4, 0x8d, 0x29, 0x3b, 0xb6, 0xd5, 0xc7, 0xe2, 0x22, 0x3f, 0x6e, 0xb5, 0xf2, 0xda,
148            0x13, 0x1d, 0x37, 0x18, 0x5b, 0xf8, 0x45, 0x59, 0x33, 0x38, 0xaf, 0xc4, 0x8a, 0x1d,
149            0x78, 0x81, 0xc8, 0xc3, 0xdb, 0xab, 0x23, 0xe1, 0x13, 0xb0, 0x04, 0xd7, 0x3c, 0x21,
150            0x0e, 0xba, 0x5d, 0xfc, 0xac, 0x4f, 0x04, 0x2d,
151        ];
152        let path = std::env::temp_dir().join("test_file_adapter_codes");
153        {
154            let mut writer = <BufBitWriter<BE, _>>::new(<WordAdapter<u64, _>>::new(
155                std::fs::File::create(&path).unwrap(),
156            ));
157            for value in &data {
158                writer.write_gamma(*value as _).unwrap();
159            }
160        }
161        {
162            let mut reader = <BufBitReader<BE, _>>::new(<WordAdapter<u32, _>>::new(
163                std::fs::File::open(&path).unwrap(),
164            ));
165            for value in &data {
166                assert_eq!(*value as u64, reader.read_gamma().unwrap());
167            }
168        }
169        {
170            let mut writer = <BufBitWriter<LE, _>>::new(<WordAdapter<u64, _>>::new(
171                std::fs::File::create(&path).unwrap(),
172            ));
173            for value in &data {
174                writer.write_gamma(*value as _).unwrap();
175            }
176        }
177        {
178            let mut reader = <BufBitReader<LE, _>>::new(<WordAdapter<u32, _>>::new(
179                std::fs::File::open(&path).unwrap(),
180            ));
181            for value in &data {
182                assert_eq!(*value as u64, reader.read_gamma().unwrap());
183            }
184        }
185    }
186}