Skip to main content

qubit_io/stream/
buffered_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::codec::{
20    BigEndian,
21    BinaryCodec,
22    ByteOrder,
23    ByteOrderSpec,
24    LittleEndian,
25};
26use crate::stream::BufferedInput;
27
28/// Buffered reader for fixed-width binary values.
29///
30/// Scalar reads decode directly from the internal input buffer whenever enough
31/// bytes are available, avoiding the per-value temporary buffer used by the
32/// extension trait helpers.
33///
34/// # Buffered state
35///
36/// This reader may prefetch bytes from the wrapped reader. As a result,
37/// [`Self::get_ref`] can observe an underlying stream position ahead of the
38/// logical position exposed by this wrapper, and [`Self::into_inner`] discards
39/// any prefetched bytes that have not been consumed.
40pub struct BufferedBinaryReader<R, O = BigEndian> {
41    input: BufferedInput<R>,
42    marker: PhantomData<fn() -> O>,
43}
44
45impl<R, O> BufferedBinaryReader<R, O>
46where
47    O: ByteOrderSpec,
48{
49    /// Creates a buffered binary reader with the default buffer capacity.
50    #[must_use]
51    #[inline]
52    pub fn new(inner: R) -> Self {
53        Self {
54            input: BufferedInput::new(inner),
55            marker: PhantomData,
56        }
57    }
58
59    /// Creates a buffered binary reader with at least `capacity` bytes.
60    #[must_use]
61    #[inline]
62    pub fn with_capacity(inner: R, capacity: usize) -> Self {
63        Self {
64            input: BufferedInput::with_capacity(inner, capacity),
65            marker: PhantomData,
66        }
67    }
68
69    /// Returns the byte order selected by this reader.
70    #[must_use]
71    #[inline]
72    pub const fn byte_order(&self) -> ByteOrder {
73        O::ORDER
74    }
75
76    /// Returns a shared reference to the underlying reader.
77    ///
78    /// The underlying reader may already be positioned past unread bytes held
79    /// in this wrapper's internal buffer.
80    #[must_use]
81    #[inline]
82    pub const fn get_ref(&self) -> &R {
83        self.input.get_ref()
84    }
85
86    /// Returns an exclusive reference to the underlying reader.
87    ///
88    /// Mutating the underlying reader directly can invalidate prefetched bytes
89    /// already held in this wrapper's internal buffer.
90    #[must_use]
91    #[inline]
92    pub fn get_mut(&mut self) -> &mut R {
93        self.input.get_mut()
94    }
95
96    /// Consumes this wrapper and returns the underlying reader.
97    ///
98    /// Any bytes already prefetched into the internal buffer but not consumed
99    /// by codec methods are discarded, matching [`std::io::BufReader`]
100    /// `into_inner` semantics.
101    #[must_use]
102    #[inline]
103    pub fn into_inner(self) -> R {
104        self.input.into_inner()
105    }
106}
107
108macro_rules! impl_value_read {
109    ($order:ty, $method:ident, $ty:ty, $doc:literal) => {
110        #[doc = $doc]
111        #[inline]
112        pub fn $method(&mut self) -> Result<$ty> {
113            type Codec = BinaryCodec<$ty, $order>;
114
115            const LEN: usize = Codec::REQUIRED_MIN_BUFFER_LEN;
116            self.input.read_fixed::<LEN, _, _>(|bytes, index| {
117                // SAFETY: `read_fixed` guarantees that `LEN` readable bytes
118                // starting at `index` are available in the internal buffer.
119                unsafe { Codec::read_unchecked(bytes, index) }
120            })
121        }
122    };
123}
124
125macro_rules! impl_for_order {
126    ($order:ty) => {
127        impl<R> BufferedBinaryReader<R, $order>
128        where
129            R: Read,
130        {
131            impl_value_read!($order, read_u8, u8, "Reads an unsigned 8-bit integer.");
132            impl_value_read!($order, read_i8, i8, "Reads a signed 8-bit integer.");
133            impl_value_read!($order, read_u16, u16, "Reads an unsigned 16-bit integer.");
134            impl_value_read!($order, read_u32, u32, "Reads an unsigned 32-bit integer.");
135            impl_value_read!($order, read_u64, u64, "Reads an unsigned 64-bit integer.");
136            impl_value_read!($order, read_u128, u128, "Reads an unsigned 128-bit integer.");
137            impl_value_read!($order, read_i16, i16, "Reads a signed 16-bit integer.");
138            impl_value_read!($order, read_i32, i32, "Reads a signed 32-bit integer.");
139            impl_value_read!($order, read_i64, i64, "Reads a signed 64-bit integer.");
140            impl_value_read!($order, read_i128, i128, "Reads a signed 128-bit integer.");
141            impl_value_read!($order, read_f32, f32, "Reads a 32-bit float.");
142            impl_value_read!($order, read_f64, f64, "Reads a 64-bit float.");
143        }
144    };
145}
146
147impl_for_order!(BigEndian);
148impl_for_order!(LittleEndian);
149
150impl<R, O> Read for BufferedBinaryReader<R, O>
151where
152    R: Read,
153{
154    /// Reads bytes from the buffered reader.
155    #[inline]
156    fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
157        self.input.read_raw(buffer)
158    }
159}
160
161impl<R, O> Seek for BufferedBinaryReader<R, O>
162where
163    R: Read + Seek,
164{
165    /// Seeks the wrapped reader and discards buffered bytes after success.
166    #[inline]
167    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
168        self.input.seek_raw(position)
169    }
170}