qubit_io/codecs/zig_zag_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::ZigZagReadExt;
19
20/// Reader wrapper for ZigZag encoded signed integers.
21///
22/// This wrapper defaults to non-strict decoding of the underlying unsigned
23/// LEB128 value. Use [`ZigZagReader::with_strict`] or
24/// [`ZigZagReader::set_strict`] to reject non-canonical underlying encodings.
25///
26/// # Examples
27/// ```
28/// use std::io::Cursor;
29///
30/// use qubit_io::{
31/// ZigZagReader,
32/// ZigZagWriter,
33/// };
34///
35/// let mut output = ZigZagWriter::new(Vec::new());
36/// output.write_i32(-123)?;
37///
38/// let mut input = ZigZagReader::with_strict(Cursor::new(output.into_inner()), true);
39/// assert_eq!(-123, input.read_i32()?);
40/// # Ok::<(), std::io::Error>(())
41/// ```
42pub struct ZigZagReader<R> {
43 inner: R,
44 strict: bool,
45}
46
47impl<R> ZigZagReader<R> {
48 /// Creates a ZigZag reader.
49 ///
50 /// # Parameters
51 /// - `inner`: Reader to wrap.
52 ///
53 /// # Returns
54 /// A new non-strict ZigZag reader.
55 #[inline]
56 pub fn new(inner: R) -> Self {
57 Self::with_strict(inner, false)
58 }
59
60 /// Creates a ZigZag reader with explicit underlying LEB128 policy.
61 ///
62 /// # Parameters
63 /// - `inner`: Reader to wrap.
64 /// - `strict`: Whether to reject non-canonical underlying LEB128 encodings.
65 ///
66 /// # Returns
67 /// A new ZigZag reader.
68 #[inline]
69 pub fn with_strict(inner: R, strict: bool) -> Self {
70 Self { inner, strict }
71 }
72
73 /// Reports whether strict underlying LEB128 decoding is enabled.
74 ///
75 /// # Returns
76 /// `true` when non-canonical underlying LEB128 encodings are rejected.
77 #[inline]
78 pub fn is_strict(&self) -> bool {
79 self.strict
80 }
81
82 /// Changes the underlying LEB128 policy used by subsequent reads.
83 ///
84 /// # Parameters
85 /// - `strict`: Whether to reject non-canonical underlying LEB128 encodings.
86 #[inline]
87 pub fn set_strict(&mut self, strict: bool) {
88 self.strict = strict;
89 }
90
91 /// Returns an immutable reference to the wrapped reader.
92 ///
93 /// # Returns
94 /// The wrapped reader reference.
95 #[inline]
96 pub fn get_ref(&self) -> &R {
97 &self.inner
98 }
99
100 /// Returns a mutable reference to the wrapped reader.
101 ///
102 /// # Returns
103 /// The wrapped reader reference.
104 #[inline]
105 pub fn get_mut(&mut self) -> &mut R {
106 &mut self.inner
107 }
108
109 /// Consumes this wrapper and returns the wrapped reader.
110 ///
111 /// # Returns
112 /// The wrapped reader.
113 #[inline]
114 pub fn into_inner(self) -> R {
115 self.inner
116 }
117}
118
119macro_rules! delegate_read {
120 ($name:ident, $read:ident, $read_strict:ident, $value:ty) => {
121 #[doc = concat!("Reads a ZigZag encoded `", stringify!($value), "`.")]
122 ///
123 /// # Errors
124 /// Returns an I/O error from the wrapped reader, or `InvalidData` for
125 /// malformed or overflowing underlying unsigned LEB128 input. In strict
126 /// mode, also returns `InvalidData` for non-canonical underlying LEB128
127 /// input.
128 #[inline]
129 pub fn $name(&mut self) -> Result<$value> {
130 if self.strict {
131 self.inner.$read_strict()
132 } else {
133 self.inner.$read()
134 }
135 }
136 };
137}
138
139impl<R> ZigZagReader<R>
140where
141 R: Read,
142{
143 delegate_read!(read_i8, read_zigzag_i8, read_zigzag_i8_strict, i8);
144 delegate_read!(read_i16, read_zigzag_i16, read_zigzag_i16_strict, i16);
145 delegate_read!(read_i32, read_zigzag_i32, read_zigzag_i32_strict, i32);
146 delegate_read!(read_i64, read_zigzag_i64, read_zigzag_i64_strict, i64);
147 delegate_read!(read_i128, read_zigzag_i128, read_zigzag_i128_strict, i128);
148 delegate_read!(
149 read_isize,
150 read_zigzag_isize,
151 read_zigzag_isize_strict,
152 isize
153 );
154}
155
156impl<R> Read for ZigZagReader<R>
157where
158 R: Read,
159{
160 #[inline]
161 fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
162 self.inner.read(buffer)
163 }
164}
165
166impl<R> BufRead for ZigZagReader<R>
167where
168 R: BufRead,
169{
170 #[inline]
171 fn fill_buf(&mut self) -> Result<&[u8]> {
172 self.inner.fill_buf()
173 }
174
175 #[inline]
176 fn consume(&mut self, amount: usize) {
177 self.inner.consume(amount);
178 }
179}
180
181impl<R> Seek for ZigZagReader<R>
182where
183 R: Seek,
184{
185 #[inline]
186 fn seek(&mut self, position: SeekFrom) -> Result<u64> {
187 self.inner.seek(position)
188 }
189}