Skip to main content

qubit_io/codecs/
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 ******************************************************************************/
10use std::io::{
11    BufRead,
12    Read,
13    Result,
14    Seek,
15    SeekFrom,
16};
17
18use crate::{
19    Leb128ReadExt,
20    StringReadExt,
21};
22
23/// Reader wrapper for LEB128 integers and LEB128 length-prefixed strings.
24///
25/// This wrapper defaults to non-strict decoding. Use [`Leb128Reader::with_strict`]
26/// or [`Leb128Reader::set_strict`] to reject non-canonical LEB128 encodings.
27///
28/// # Examples
29/// ```
30/// use std::io::Cursor;
31///
32/// use qubit_io::{
33///     Leb128Reader,
34///     Leb128Writer,
35/// };
36///
37/// let mut output = Leb128Writer::new(Vec::new());
38/// output.write_u16(300)?;
39///
40/// let mut input = Leb128Reader::with_strict(Cursor::new(output.into_inner()), true);
41/// assert_eq!(300, input.read_u16()?);
42/// # Ok::<(), std::io::Error>(())
43/// ```
44pub struct Leb128Reader<R> {
45    inner: R,
46    strict: bool,
47}
48
49impl<R> Leb128Reader<R> {
50    /// Creates a LEB128 reader.
51    ///
52    /// # Parameters
53    /// - `inner`: Reader to wrap.
54    ///
55    /// # Returns
56    /// A new non-strict LEB128 reader.
57    #[inline]
58    pub fn new(inner: R) -> Self {
59        Self::with_strict(inner, false)
60    }
61
62    /// Creates a LEB128 reader with explicit canonical-decoding policy.
63    ///
64    /// # Parameters
65    /// - `inner`: Reader to wrap.
66    /// - `strict`: Whether to reject non-canonical LEB128 encodings.
67    ///
68    /// # Returns
69    /// A new LEB128 reader.
70    #[inline]
71    pub fn with_strict(inner: R, strict: bool) -> Self {
72        Self { inner, strict }
73    }
74
75    /// Reports whether strict canonical LEB128 decoding is enabled.
76    ///
77    /// # Returns
78    /// `true` when non-canonical encodings are rejected.
79    #[inline]
80    pub fn is_strict(&self) -> bool {
81        self.strict
82    }
83
84    /// Changes the canonical-decoding policy used by subsequent reads.
85    ///
86    /// # Parameters
87    /// - `strict`: Whether to reject non-canonical LEB128 encodings.
88    #[inline]
89    pub fn set_strict(&mut self, strict: bool) {
90        self.strict = strict;
91    }
92
93    /// Returns an immutable reference to the wrapped reader.
94    ///
95    /// # Returns
96    /// The wrapped reader reference.
97    #[inline]
98    pub fn get_ref(&self) -> &R {
99        &self.inner
100    }
101
102    /// Returns a mutable reference to the wrapped reader.
103    ///
104    /// # Returns
105    /// The wrapped reader reference.
106    #[inline]
107    pub fn get_mut(&mut self) -> &mut R {
108        &mut self.inner
109    }
110
111    /// Consumes this wrapper and returns the wrapped reader.
112    ///
113    /// # Returns
114    /// The wrapped reader.
115    #[inline]
116    pub fn into_inner(self) -> R {
117        self.inner
118    }
119}
120
121macro_rules! delegate_read {
122    ($name:ident, $read:ident, $read_strict:ident, $value:ty) => {
123        #[doc = concat!("Reads a LEB128 `", stringify!($value), "`.")]
124        ///
125        /// # Errors
126        /// Returns an I/O error from the wrapped reader, or `InvalidData` for
127        /// malformed or overflowing LEB128 input. In strict mode, also returns
128        /// `InvalidData` for non-canonical LEB128 input.
129        #[inline]
130        pub fn $name(&mut self) -> Result<$value> {
131            if self.strict {
132                self.inner.$read_strict()
133            } else {
134                self.inner.$read()
135            }
136        }
137    };
138}
139
140impl<R> Leb128Reader<R>
141where
142    R: Read,
143{
144    delegate_read!(read_u8, read_uleb_u8, read_uleb_u8_strict, u8);
145    delegate_read!(read_u16, read_uleb_u16, read_uleb_u16_strict, u16);
146    delegate_read!(read_u32, read_uleb_u32, read_uleb_u32_strict, u32);
147    delegate_read!(read_u64, read_uleb_u64, read_uleb_u64_strict, u64);
148    delegate_read!(read_u128, read_uleb_u128, read_uleb_u128_strict, u128);
149    delegate_read!(read_usize, read_uleb_usize, read_uleb_usize_strict, usize);
150    delegate_read!(read_i8, read_sleb_i8, read_sleb_i8_strict, i8);
151    delegate_read!(read_i16, read_sleb_i16, read_sleb_i16_strict, i16);
152    delegate_read!(read_i32, read_sleb_i32, read_sleb_i32_strict, i32);
153    delegate_read!(read_i64, read_sleb_i64, read_sleb_i64_strict, i64);
154    delegate_read!(read_i128, read_sleb_i128, read_sleb_i128_strict, i128);
155    delegate_read!(read_isize, read_sleb_isize, read_sleb_isize_strict, isize);
156
157    /// Reads a UTF-8 string with an unsigned LEB128 byte-length prefix.
158    ///
159    /// # Parameters
160    /// - `max_len`: Maximum accepted UTF-8 payload length in bytes.
161    ///
162    /// # Errors
163    /// Returns an I/O error from the wrapped reader, or `InvalidData` when the
164    /// encoded length exceeds `max_len` or the payload is not valid UTF-8. In
165    /// strict mode, also returns `InvalidData` when the length prefix is
166    /// non-canonical.
167    #[inline]
168    pub fn read_utf8_string(&mut self, max_len: usize) -> Result<String> {
169        if self.strict {
170            self.inner.read_utf8_string_uleb_strict(max_len)
171        } else {
172            self.inner.read_utf8_string_uleb(max_len)
173        }
174    }
175}
176
177impl<R> Read for Leb128Reader<R>
178where
179    R: Read,
180{
181    #[inline]
182    fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
183        self.inner.read(buffer)
184    }
185}
186
187impl<R> BufRead for Leb128Reader<R>
188where
189    R: BufRead,
190{
191    #[inline]
192    fn fill_buf(&mut self) -> Result<&[u8]> {
193        self.inner.fill_buf()
194    }
195
196    #[inline]
197    fn consume(&mut self, amount: usize) {
198        self.inner.consume(amount);
199    }
200}
201
202impl<R> Seek for Leb128Reader<R>
203where
204    R: Seek,
205{
206    #[inline]
207    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
208        self.inner.seek(position)
209    }
210}