Skip to main content

qubit_io/stream/
binary_reader.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10
11use core::marker::PhantomData;
12use std::io::{
13    Read,
14    Result,
15    Seek,
16    SeekFrom,
17};
18
19use crate::ReadExt;
20use crate::codec::{
21    BigEndian,
22    BinaryCodec,
23    ByteOrder,
24    ByteOrderSpec,
25    LittleEndian,
26};
27use crate::util::read_utf8_payload;
28
29/// Reader wrapper for fixed-width binary values.
30///
31/// The byte order is selected by the `O` type parameter. Use
32/// `BinaryReader<R, BigEndian>` for big-endian data and
33/// `BinaryReader<R, LittleEndian>` for little-endian data.
34pub struct BinaryReader<R, O = BigEndian> {
35    inner: R,
36    buffer: [u8; 16],
37    marker: PhantomData<fn() -> O>,
38}
39
40impl<R, O> BinaryReader<R, O>
41where
42    O: ByteOrderSpec,
43{
44    /// Creates a binary reader.
45    ///
46    /// # Parameters
47    ///
48    /// - `inner`: Underlying byte reader.
49    ///
50    /// # Returns
51    ///
52    /// Returns a reader using the byte order selected by `O`.
53    #[must_use]
54    #[inline]
55    pub const fn new(inner: R) -> Self {
56        Self {
57            inner,
58            buffer: [0; 16],
59            marker: PhantomData,
60        }
61    }
62
63    /// Returns the byte order selected by this reader.
64    #[must_use]
65    #[inline]
66    pub const fn byte_order(&self) -> ByteOrder {
67        O::ORDER
68    }
69
70    /// Returns a shared reference to the underlying reader.
71    #[must_use]
72    #[inline]
73    pub const fn get_ref(&self) -> &R {
74        &self.inner
75    }
76
77    /// Returns an exclusive reference to the underlying reader.
78    #[must_use]
79    #[inline]
80    pub fn get_mut(&mut self) -> &mut R {
81        &mut self.inner
82    }
83
84    /// Consumes this wrapper and returns the underlying reader.
85    #[must_use]
86    #[inline]
87    pub fn into_inner(self) -> R {
88        self.inner
89    }
90}
91
92macro_rules! impl_value_read {
93    ($order:ty, $method:ident, $ty:ty, $doc:literal) => {
94        #[doc = $doc]
95        #[inline]
96        pub fn $method(&mut self) -> Result<$ty> {
97            type Codec = BinaryCodec<$ty, $order>;
98
99            const LEN: usize = Codec::REQUIRED_MIN_BUFFER_LEN;
100            // SAFETY: `LEN` is declared by the codec and fits the fixed internal buffer.
101            unsafe {
102                ReadExt::read_exact_unchecked(&mut self.inner, &mut self.buffer, 0, LEN)?;
103                Ok(Codec::read_unchecked(&self.buffer, 0))
104            }
105        }
106    };
107}
108
109macro_rules! impl_for_order {
110    ($order:ty) => {
111        impl<R> BinaryReader<R, $order>
112        where
113            R: Read,
114        {
115            impl_value_read!($order, read_u8, u8, "Reads an unsigned 8-bit integer.");
116            impl_value_read!($order, read_i8, i8, "Reads a signed 8-bit integer.");
117            impl_value_read!($order, read_u16, u16, "Reads an unsigned 16-bit integer.");
118            impl_value_read!($order, read_u32, u32, "Reads an unsigned 32-bit integer.");
119            impl_value_read!($order, read_u64, u64, "Reads an unsigned 64-bit integer.");
120            impl_value_read!($order, read_u128, u128, "Reads an unsigned 128-bit integer.");
121            impl_value_read!($order, read_i16, i16, "Reads a signed 16-bit integer.");
122            impl_value_read!($order, read_i32, i32, "Reads a signed 32-bit integer.");
123            impl_value_read!($order, read_i64, i64, "Reads a signed 64-bit integer.");
124            impl_value_read!($order, read_i128, i128, "Reads a signed 128-bit integer.");
125            impl_value_read!($order, read_f32, f32, "Reads a 32-bit float.");
126            impl_value_read!($order, read_f64, f64, "Reads a 64-bit float.");
127
128            /// Reads a UTF-8 string prefixed by a 16-bit byte length.
129            ///
130            /// # Parameters
131            ///
132            /// - `max_len`: Maximum accepted UTF-8 payload length in bytes.
133            ///
134            /// # Errors
135            ///
136            /// Returns [`std::io::ErrorKind::InvalidData`] when the encoded length exceeds
137            /// `max_len` or when the payload is not valid UTF-8.
138            #[inline]
139            pub fn read_utf8_string_u16(&mut self, max_len: usize) -> Result<String> {
140                let len = usize::from(self.read_u16()?);
141                read_utf8_payload(&mut self.inner, len, max_len)
142            }
143
144            /// Reads a UTF-8 string prefixed by a 32-bit byte length.
145            ///
146            /// # Parameters
147            ///
148            /// - `max_len`: Maximum accepted UTF-8 payload length in bytes.
149            ///
150            /// # Errors
151            ///
152            /// Returns [`std::io::ErrorKind::InvalidData`] when the encoded length exceeds
153            /// `max_len` or when the payload is not valid UTF-8.
154            #[inline]
155            pub fn read_utf8_string_u32(&mut self, max_len: usize) -> Result<String> {
156                let len = self.read_u32()? as usize;
157                read_utf8_payload(&mut self.inner, len, max_len)
158            }
159        }
160    };
161}
162
163impl_for_order!(BigEndian);
164impl_for_order!(LittleEndian);
165
166impl<R, O> Read for BinaryReader<R, O>
167where
168    R: Read,
169{
170    /// Reads bytes from the wrapped reader.
171    ///
172    /// # Parameters
173    ///
174    /// - `buffer`: Destination byte buffer.
175    ///
176    /// # Returns
177    ///
178    /// Returns the number of bytes read.
179    ///
180    /// # Errors
181    ///
182    /// Returns the I/O error reported by the wrapped reader.
183    #[inline]
184    fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
185        self.inner.read(buffer)
186    }
187}
188
189impl<R, O> Seek for BinaryReader<R, O>
190where
191    R: Seek,
192{
193    /// Seeks the wrapped reader.
194    ///
195    /// # Parameters
196    ///
197    /// - `position`: Target seek position.
198    ///
199    /// # Returns
200    ///
201    /// Returns the new stream position.
202    ///
203    /// # Errors
204    ///
205    /// Returns the seek error reported by the wrapped reader.
206    #[inline]
207    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
208        self.inner.seek(position)
209    }
210}