Skip to main content

base64_ng/stream/
encoder.rs

1use super::{EncoderDriver, OutputQueue, redacted_inner_state, stream_encoder_failed_error};
2use crate::{Alphabet, Engine};
3use std::io::{self, Write};
4
5/// A streaming Base64 encoder for `std::io::Write`.
6///
7/// Like any [`Write`] implementation, [`Write::write`] may accept only
8/// part of the provided input. Accepted input may be held as encoded
9/// output until [`Write::flush`], [`Self::try_finish`], [`Self::finish`],
10/// or a later write drains the wrapped writer. Use [`Write::write_all`]
11/// when the whole input slice must be consumed.
12pub struct Encoder<W, A, const PAD: bool>
13where
14    A: Alphabet,
15{
16    inner: Option<W>,
17    engine: Engine<A, PAD>,
18    driver: EncoderDriver,
19    output: OutputQueue<1024>,
20    finalized: bool,
21    failed: bool,
22}
23
24impl<W, A, const PAD: bool> Encoder<W, A, PAD>
25where
26    A: Alphabet,
27{
28    /// Creates a new streaming encoder.
29    #[must_use]
30    pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
31        Self {
32            inner: Some(inner),
33            engine,
34            driver: EncoderDriver::new::<A, PAD>(),
35            output: OutputQueue::new(),
36            finalized: false,
37            failed: false,
38        }
39    }
40
41    /// Returns a shared reference to the wrapped writer.
42    #[must_use]
43    pub fn get_ref(&self) -> &W {
44        self.inner_ref()
45    }
46
47    /// Returns a mutable reference to the wrapped writer.
48    pub fn get_mut(&mut self) -> &mut W {
49        self.inner_mut()
50    }
51
52    /// Returns the Base64 engine used by this adapter.
53    #[must_use]
54    pub const fn engine(&self) -> Engine<A, PAD> {
55        self.engine
56    }
57
58    /// Returns whether this adapter uses padded Base64.
59    #[must_use]
60    pub const fn is_padded(&self) -> bool {
61        PAD
62    }
63
64    /// Returns the number of raw input bytes currently buffered until a
65    /// complete 3-byte Base64 encode quantum is available.
66    #[must_use]
67    pub const fn pending_len(&self) -> usize {
68        self.driver.pending_input_len()
69    }
70
71    /// Returns whether this encoder currently holds a partial input
72    /// quantum.
73    #[must_use]
74    pub const fn has_pending_input(&self) -> bool {
75        self.pending_len() != 0
76    }
77
78    /// Returns how many additional input bytes are needed to complete the
79    /// currently buffered encode quantum.
80    ///
81    /// Returns `0` when no partial input quantum is buffered.
82    #[must_use]
83    pub const fn pending_input_needed_len(&self) -> usize {
84        if self.has_pending_input() {
85            3 - self.pending_len()
86        } else {
87            0
88        }
89    }
90
91    /// Returns the number of encoded bytes buffered for the wrapped
92    /// writer after a previous write or flush could not fully drain them.
93    #[must_use]
94    pub const fn buffered_output_len(&self) -> usize {
95        self.output.len()
96    }
97
98    /// Returns the maximum number of encoded bytes this adapter can buffer
99    /// before returning bytes to the caller.
100    #[must_use]
101    pub const fn buffered_output_capacity(&self) -> usize {
102        self.output.capacity()
103    }
104
105    /// Returns how many more encoded bytes can be buffered before this
106    /// adapter must drain the wrapped writer.
107    #[must_use]
108    pub const fn buffered_output_remaining_capacity(&self) -> usize {
109        self.output.available_capacity()
110    }
111
112    /// Returns whether this encoder has encoded output waiting to be
113    /// written to the wrapped writer.
114    #[must_use]
115    pub const fn has_buffered_output(&self) -> bool {
116        !self.output.is_empty()
117    }
118
119    /// Returns whether this encoder has been finalized.
120    ///
121    /// Once this returns `true`, later writes return an error.
122    #[must_use]
123    pub const fn is_finalized(&self) -> bool {
124        self.finalized
125    }
126
127    /// Returns whether this encoder has failed closed after an unrecoverable
128    /// internal encoding or buffering error.
129    ///
130    /// Ordinary wrapped-writer I/O errors are retryable and do not set this
131    /// flag. Once this returns `true`, later writes, flushes, and finalization
132    /// attempts return an error. The unchecked [`Self::into_inner`] method can
133    /// still be used for explicit recovery of the wrapped writer.
134    #[must_use]
135    pub const fn is_failed(&self) -> bool {
136        self.failed
137    }
138
139    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
140    /// writer without discarding pending input.
141    #[must_use]
142    pub const fn can_into_inner(&self) -> bool {
143        !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
144    }
145
146    /// Consumes the encoder without flushing pending input.
147    ///
148    /// Prefer [`Self::finish`] when the encoded output must be complete.
149    #[must_use]
150    pub fn into_inner(mut self) -> W {
151        self.take_inner()
152    }
153
154    /// Consumes the encoder only when no partial input quantum is buffered.
155    ///
156    /// This does not flush or finalize the wrapped writer. It is a checked
157    /// alternative to [`Self::into_inner`] for callers that want to avoid
158    /// accidentally discarding pending input bytes.
159    #[allow(clippy::result_large_err)]
160    pub fn try_into_inner(mut self) -> Result<W, Self> {
161        if !self.can_into_inner() {
162            return Err(self);
163        }
164        Ok(self.take_inner())
165    }
166
167    fn inner_ref(&self) -> &W {
168        match &self.inner {
169            Some(inner) => inner,
170            None => unreachable!("stream encoder inner writer was already taken"),
171        }
172    }
173
174    fn inner_mut(&mut self) -> &mut W {
175        match &mut self.inner {
176            Some(inner) => inner,
177            None => unreachable!("stream encoder inner writer was already taken"),
178        }
179    }
180
181    fn take_inner(&mut self) -> W {
182        match self.inner.take() {
183            Some(inner) => inner,
184            None => unreachable!("stream encoder inner writer was already taken"),
185        }
186    }
187
188    fn clear_pending(&mut self) {
189        self.driver.wipe();
190    }
191
192    fn clear_output(&mut self) {
193        self.output.clear_all();
194    }
195}
196
197impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
198where
199    A: Alphabet,
200{
201    fn drop(&mut self) {
202        self.clear_pending();
203        self.clear_output();
204    }
205}
206
207impl<W, A, const PAD: bool> core::fmt::Debug for Encoder<W, A, PAD>
208where
209    A: Alphabet,
210{
211    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
212        formatter
213            .debug_struct("Encoder")
214            .field("inner", &redacted_inner_state(self.inner.is_some()))
215            .field("engine", &self.engine)
216            .field("driver", &"<redacted>")
217            .field("pending", &"<redacted>")
218            .field("pending_len", &self.pending_len())
219            .field("pending_input_needed_len", &self.pending_input_needed_len())
220            .field("buffered_output_len", &self.output.len())
221            .field("buffered_output_capacity", &self.output.capacity())
222            .field(
223                "buffered_output_remaining_capacity",
224                &self.output.available_capacity(),
225            )
226            .field("can_into_inner", &self.can_into_inner())
227            .field("finalized", &self.finalized)
228            .field("failed", &self.failed)
229            .finish()
230    }
231}
232
233impl<W, A, const PAD: bool> Encoder<W, A, PAD>
234where
235    W: Write,
236    A: Alphabet,
237{
238    /// Writes any pending input and flushes the wrapped writer without
239    /// consuming this encoder.
240    ///
241    /// After this succeeds, [`Self::pending_len`] returns `0`, later
242    /// writes are rejected, and [`Self::finish`] can still be used to
243    /// recover the wrapped writer.
244    /// This is useful when a caller needs to finalize a framed payload
245    /// while keeping the stream adapter available for diagnostics or
246    /// explicit recovery.
247    pub fn try_finish(&mut self) -> io::Result<()> {
248        if self.failed {
249            return Err(stream_encoder_failed_error());
250        }
251        if !self.finalized {
252            self.queue_pending_final()?;
253            self.finalized = true;
254        }
255        self.flush()
256    }
257
258    /// Writes any pending input, flushes the wrapped writer, and returns it.
259    pub fn finish(mut self) -> io::Result<W> {
260        self.try_finish()?;
261        Ok(self.take_inner())
262    }
263
264    fn queue_pending_final(&mut self) -> io::Result<()> {
265        let mut encoded = [0u8; 4];
266        let step = match self.driver.finish(&mut encoded) {
267            Ok(step) => step,
268            Err(err) => {
269                crate::wipe_bytes(&mut encoded);
270                self.failed = true;
271                return Err(err);
272            }
273        };
274        let produced = step.progress().output_produced();
275        let result = self.output.push_slice(&encoded[..produced]);
276        crate::wipe_bytes(&mut encoded);
277        if result.is_err() {
278            self.failed = true;
279        }
280        result?;
281        Ok(())
282    }
283
284    fn drain_output(&mut self) -> io::Result<()> {
285        let mut chunk = [0u8; 1024];
286        while !self.output.is_empty() {
287            let pending = self.output.copy_front(&mut chunk);
288            let result = self.inner_mut().write(&chunk[..pending]);
289            crate::wipe_bytes(&mut chunk[..pending]);
290            match result {
291                Ok(0) => {
292                    return Err(io::Error::new(
293                        io::ErrorKind::WriteZero,
294                        "base64 stream encoder could not drain buffered output",
295                    ));
296                }
297                Ok(written) => {
298                    if written > pending {
299                        self.failed = true;
300                        return Err(io::Error::new(
301                            io::ErrorKind::InvalidData,
302                            "wrapped writer reported more bytes than provided",
303                        ));
304                    }
305                    self.output.discard_front(written);
306                }
307                Err(err) => return Err(err),
308            }
309        }
310
311        Ok(())
312    }
313}
314
315impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
316where
317    W: Write,
318    A: Alphabet,
319{
320    fn write(&mut self, input: &[u8]) -> io::Result<usize> {
321        if self.failed {
322            return Err(stream_encoder_failed_error());
323        }
324        self.drain_output()?;
325        if self.finalized {
326            return Err(io::Error::new(
327                io::ErrorKind::InvalidInput,
328                "base64 stream encoder received input after finalization",
329            ));
330        }
331        if input.is_empty() {
332            return Ok(0);
333        }
334
335        let take = input.len().min(768);
336        let mut encoded = [0u8; 1024];
337        let step = match self.driver.update(&input[..take], &mut encoded) {
338            Ok(step) => step,
339            Err(err) => {
340                crate::wipe_bytes(&mut encoded);
341                self.failed = true;
342                return Err(err);
343            }
344        };
345        let progress = step.progress();
346        let result = self
347            .output
348            .push_slice(&encoded[..progress.output_produced()]);
349        crate::wipe_bytes(&mut encoded);
350        if result.is_err() {
351            self.failed = true;
352        }
353        result?;
354        Ok(progress.input_consumed())
355    }
356
357    fn flush(&mut self) -> io::Result<()> {
358        if self.failed {
359            return Err(stream_encoder_failed_error());
360        }
361        self.drain_output()?;
362        self.inner_mut().flush()
363    }
364}