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}