Skip to main content

qubit_io/stream/
buffered_leb128_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    Error,
14    ErrorKind,
15    Read,
16    Result,
17    Seek,
18    SeekFrom,
19};
20
21use crate::codec::{
22    DecodePolicy,
23    Leb128Codec,
24    Leb128DecodeError,
25    NonStrict,
26    Strict,
27};
28use crate::stream::BufferedInput;
29use crate::util::read_utf8_payload;
30
31/// Buffered reader for LEB128 integers.
32///
33/// Values are decoded directly from the internal input buffer while the codec
34/// scans for the LEB128 terminating byte.
35///
36/// # Buffered state
37///
38/// This reader may prefetch bytes from the wrapped reader. As a result,
39/// [`Self::inner`] can observe an underlying stream position ahead of the
40/// logical position exposed by this wrapper, and [`Self::into_inner`] discards
41/// any prefetched bytes that have not been consumed.
42///
43/// # Target-width integers
44///
45/// `usize` and `isize` methods use the current Rust target's pointer width.
46/// Prefer fixed-width integer methods such as `read_u64` or `read_i64` for
47/// persistent files and cross-platform protocols.
48pub struct BufferedLeb128Reader<R, P = NonStrict> {
49    input: BufferedInput<R>,
50    marker: PhantomData<fn() -> P>,
51}
52
53impl<R, P> BufferedLeb128Reader<R, P>
54where
55    P: DecodePolicy,
56{
57    /// Creates a buffered LEB128 reader with the default buffer capacity.
58    #[must_use]
59    #[inline]
60    pub fn new(inner: R) -> Self {
61        Self {
62            input: BufferedInput::new(inner),
63            marker: PhantomData,
64        }
65    }
66
67    /// Creates a buffered LEB128 reader with at least `capacity` bytes.
68    #[must_use]
69    #[inline]
70    pub fn with_capacity(inner: R, capacity: usize) -> Self {
71        Self {
72            input: BufferedInput::with_capacity(inner, capacity),
73            marker: PhantomData,
74        }
75    }
76
77    /// Returns whether this reader rejects non-canonical encodings.
78    #[must_use]
79    #[inline]
80    pub const fn is_strict(&self) -> bool {
81        P::STRICT
82    }
83
84    /// Returns a shared reference to the underlying reader.
85    ///
86    /// The underlying reader may already be positioned past unread bytes held
87    /// in this wrapper's internal buffer.
88    #[must_use]
89    #[inline]
90    pub const fn inner(&self) -> &R {
91        self.input.inner()
92    }
93
94    /// Returns an exclusive reference to the underlying reader.
95    ///
96    /// Mutating the underlying reader directly can invalidate prefetched bytes
97    /// already held in this wrapper's internal buffer.
98    #[must_use]
99    #[inline]
100    pub fn inner_mut(&mut self) -> &mut R {
101        self.input.inner_mut()
102    }
103
104    /// Consumes this wrapper and returns the underlying reader.
105    ///
106    /// Any bytes already prefetched into the internal buffer but not consumed
107    /// by codec methods are discarded.
108    #[must_use]
109    #[inline]
110    pub fn into_inner(self) -> R {
111        self.input.into_inner()
112    }
113}
114
115macro_rules! impl_read_value {
116    ($policy:ty, $method:ident, $ty:ty, $doc:literal) => {
117        #[doc = $doc]
118        #[inline]
119        pub fn $method(&mut self) -> Result<$ty> {
120            type Codec = Leb128Codec<$ty, $policy>;
121
122            self.input
123                .read_variable_decoded::<{ Codec::REQUIRED_MIN_BUFFER_LEN }, _, _, _, _>(
124                    |bytes, index, available| {
125                        // SAFETY: `read_variable_decoded` only passes bytes already
126                        // present in the internal buffer and caps `available` at
127                        // the codec maximum width.
128                        unsafe { Codec::read_available_unchecked(bytes, index, available) }
129                    },
130                    map_leb128_decode_error,
131                )
132        }
133    };
134}
135
136macro_rules! impl_for_policy {
137    ($policy:ty) => {
138        impl<R> BufferedLeb128Reader<R, $policy>
139        where
140            R: Read,
141        {
142            impl_read_value!($policy, read_u8, u8, "Reads an unsigned LEB128 `u8`.");
143            impl_read_value!($policy, read_u16, u16, "Reads an unsigned LEB128 `u16`.");
144            impl_read_value!($policy, read_u32, u32, "Reads an unsigned LEB128 `u32`.");
145            impl_read_value!($policy, read_u64, u64, "Reads an unsigned LEB128 `u64`.");
146            impl_read_value!($policy, read_u128, u128, "Reads an unsigned LEB128 `u128`.");
147            impl_read_value!($policy, read_usize, usize, "Reads an unsigned LEB128 `usize`.");
148            impl_read_value!($policy, read_i8, i8, "Reads a signed LEB128 `i8`.");
149            impl_read_value!($policy, read_i16, i16, "Reads a signed LEB128 `i16`.");
150            impl_read_value!($policy, read_i32, i32, "Reads a signed LEB128 `i32`.");
151            impl_read_value!($policy, read_i64, i64, "Reads a signed LEB128 `i64`.");
152            impl_read_value!($policy, read_i128, i128, "Reads a signed LEB128 `i128`.");
153            impl_read_value!($policy, read_isize, isize, "Reads a signed LEB128 `isize`.");
154
155            /// Reads a UTF-8 string prefixed by an unsigned LEB128 byte length.
156            ///
157            /// The length prefix is decoded as `usize`, so this format is
158            /// target-width dependent. Prefer a fixed-width length prefix for
159            /// persistent files and cross-platform protocols.
160            #[inline]
161            pub fn read_utf8_string(&mut self, max_len: usize) -> Result<String> {
162                let len = self.read_usize()?;
163                read_utf8_payload(&mut self.input, len, max_len)
164            }
165        }
166    };
167}
168
169impl_for_policy!(NonStrict);
170impl_for_policy!(Strict);
171
172impl<R, P> Read for BufferedLeb128Reader<R, P>
173where
174    R: Read,
175{
176    /// Reads bytes from the buffered reader.
177    #[inline]
178    fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
179        self.input.read_raw(buffer)
180    }
181}
182
183impl<R, P> Seek for BufferedLeb128Reader<R, P>
184where
185    R: Read + Seek,
186{
187    /// Seeks the wrapped reader and discards buffered bytes after success.
188    #[inline]
189    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
190        self.input.seek_raw(position)
191    }
192}
193
194/// Converts a LEB128 decode error into an I/O error.
195#[inline]
196fn map_leb128_decode_error(error: Leb128DecodeError) -> Error {
197    Error::new(ErrorKind::InvalidData, error)
198}