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}