Skip to main content

qubit_io/stream/
buffered_leb128_writer.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 std::io::{
12    Result,
13    Seek,
14    SeekFrom,
15    Write,
16};
17
18use crate::codec::{
19    Leb128Codec,
20    NonStrict,
21};
22use crate::stream::BufferedOutput;
23
24/// Buffered writer for canonical LEB128 integers.
25///
26/// Values are encoded directly into the internal output buffer and flushed to
27/// the wrapped writer in larger chunks.
28///
29/// # Flush contract
30///
31/// Pending buffered bytes are not flushed from [`Drop`]. Call [`Write::flush`]
32/// or [`Self::into_inner`] to guarantee that all bytes reach the wrapped
33/// writer. [`Self::inner`] and [`Self::inner_mut`] can observe the wrapped
34/// writer before pending bytes have been flushed.
35///
36/// # Target-width integers
37///
38/// `usize` and `isize` methods use the current Rust target's pointer width.
39/// Prefer fixed-width integer methods such as `write_u64` or `write_i64` for
40/// persistent files and cross-platform protocols.
41pub struct BufferedLeb128Writer<W> {
42    output: BufferedOutput<W>,
43}
44
45impl<W> BufferedLeb128Writer<W> {
46    /// Creates a buffered LEB128 writer with the default buffer capacity.
47    #[must_use]
48    #[inline]
49    pub fn new(inner: W) -> Self {
50        Self {
51            output: BufferedOutput::new(inner),
52        }
53    }
54
55    /// Creates a buffered LEB128 writer with at least `capacity` bytes.
56    #[must_use]
57    #[inline]
58    pub fn with_capacity(inner: W, capacity: usize) -> Self {
59        Self {
60            output: BufferedOutput::with_capacity(inner, capacity),
61        }
62    }
63
64    /// Returns a shared reference to the underlying writer.
65    ///
66    /// Pending bytes may still be held in this wrapper's internal buffer.
67    #[must_use]
68    #[inline]
69    pub const fn inner(&self) -> &W {
70        self.output.inner()
71    }
72
73    /// Returns an exclusive reference to the underlying writer.
74    ///
75    /// Pending bytes may still be held in this wrapper's internal buffer.
76    /// Flush first if the underlying writer must observe all previous writes.
77    #[must_use]
78    #[inline]
79    pub fn inner_mut(&mut self) -> &mut W {
80        self.output.inner_mut()
81    }
82}
83
84impl<W> BufferedLeb128Writer<W>
85where
86    W: Write,
87{
88    /// Flushes pending bytes and returns the underlying writer.
89    #[inline]
90    pub fn into_inner(self) -> Result<W> {
91        self.output.into_inner()
92    }
93
94    /// Writes a UTF-8 string prefixed by an unsigned LEB128 byte length.
95    ///
96    /// The length prefix is encoded as `usize`, so this format is target-width
97    /// dependent. Prefer a fixed-width length prefix for persistent files and
98    /// cross-platform protocols.
99    #[inline]
100    pub fn write_utf8_string(&mut self, value: &str) -> Result<()> {
101        self.write_usize(value.len())?;
102        self.output.write_all_buffered(value.as_bytes())
103    }
104}
105
106macro_rules! impl_write_value {
107    ($method:ident, $ty:ty, $doc:literal) => {
108        #[doc = $doc]
109        #[inline]
110        pub fn $method(&mut self, value: $ty) -> Result<()> {
111            type Codec = Leb128Codec<$ty, NonStrict>;
112
113            self.output
114                .write_encoded(Codec::REQUIRED_MIN_BUFFER_LEN, value, |bytes, index, value| {
115                    // SAFETY: `write_encoded` guarantees enough writable bytes
116                    // for the codec-declared maximum encoded width.
117                    unsafe { Codec::write_unchecked(bytes, index, value) }
118                })
119        }
120    };
121}
122
123impl<W> BufferedLeb128Writer<W>
124where
125    W: Write,
126{
127    impl_write_value!(write_u8, u8, "Writes an unsigned LEB128 `u8`.");
128    impl_write_value!(write_u16, u16, "Writes an unsigned LEB128 `u16`.");
129    impl_write_value!(write_u32, u32, "Writes an unsigned LEB128 `u32`.");
130    impl_write_value!(write_u64, u64, "Writes an unsigned LEB128 `u64`.");
131    impl_write_value!(write_u128, u128, "Writes an unsigned LEB128 `u128`.");
132    impl_write_value!(write_usize, usize, "Writes an unsigned LEB128 `usize`.");
133    impl_write_value!(write_i8, i8, "Writes a signed LEB128 `i8`.");
134    impl_write_value!(write_i16, i16, "Writes a signed LEB128 `i16`.");
135    impl_write_value!(write_i32, i32, "Writes a signed LEB128 `i32`.");
136    impl_write_value!(write_i64, i64, "Writes a signed LEB128 `i64`.");
137    impl_write_value!(write_i128, i128, "Writes a signed LEB128 `i128`.");
138    impl_write_value!(write_isize, isize, "Writes a signed LEB128 `isize`.");
139}
140
141impl<W> Write for BufferedLeb128Writer<W>
142where
143    W: Write,
144{
145    /// Writes bytes through the internal buffer.
146    #[inline]
147    fn write(&mut self, buffer: &[u8]) -> Result<usize> {
148        self.output.write_raw(buffer)
149    }
150
151    /// Writes all bytes through the internal buffer.
152    #[inline]
153    fn write_all(&mut self, buffer: &[u8]) -> Result<()> {
154        self.output.write_all_buffered(buffer)
155    }
156
157    /// Flushes the internal buffer and then the wrapped writer.
158    #[inline]
159    fn flush(&mut self) -> Result<()> {
160        self.output.flush_all()
161    }
162}
163
164impl<W> Seek for BufferedLeb128Writer<W>
165where
166    W: Write + Seek,
167{
168    /// Flushes pending bytes before seeking the wrapped writer.
169    #[inline]
170    fn seek(&mut self, position: SeekFrom) -> Result<u64> {
171        self.output.seek_raw(position)
172    }
173}