Skip to main content

base64_ng/
stream.rs

1//! Streaming Base64 wrappers for `std::io`.
2//!
3//! Decoder adapters fail closed after malformed Base64 input. Encoder writer
4//! adapters also expose failed-state diagnostics for unrecoverable internal
5//! queue/encoding errors. Use
6//! `is_failed()` for diagnostics; unchecked `into_inner()` remains
7//! available when the wrapped reader or writer must be explicitly
8//! recovered after a decode error.
9//!
10//! # Security
11//!
12//! Streaming decoders use the normal strict decode path. They preserve
13//! localized I/O-style errors and are not constant-time decoders. For
14//! secret-bearing frames where timing posture matters, collect the complete
15//! framed payload first and then use `base64_ng::ct`:
16//!
17//! ```no_run
18//! use std::io::Read;
19//! use base64_ng::ct;
20//!
21//! const MAX_FRAME: usize = 4096;
22//!
23//! # fn decode_secret_frame<R: Read>(mut reader: R) -> Result<(), Box<dyn std::error::Error>> {
24//! let mut frame = Vec::new();
25//! reader.read_to_end(&mut frame)?;
26//! let decoded = ct::STANDARD.decode_buffer::<MAX_FRAME>(&frame)?;
27//! # let _ = decoded;
28//! # Ok(())
29//! # }
30//! ```
31//!
32//! ```
33//! use std::io::{Read, Write};
34//! use base64_ng::{STANDARD, stream::{Decoder, DecoderReader, Encoder, EncoderReader}};
35//!
36//! let mut encoder = Encoder::new(Vec::new(), STANDARD);
37//! encoder.write_all(b"he").unwrap();
38//! encoder.write_all(b"llo").unwrap();
39//! let encoded = encoder.finish().unwrap();
40//! assert_eq!(encoded, b"aGVsbG8=");
41//!
42//! let mut reader = EncoderReader::new(&b"hello"[..], STANDARD);
43//! let mut encoded = String::new();
44//! reader.read_to_string(&mut encoded).unwrap();
45//! assert_eq!(encoded, "aGVsbG8=");
46//!
47//! let mut decoder = Decoder::new(Vec::new(), STANDARD);
48//! decoder.write_all(b"aGVs").unwrap();
49//! decoder.write_all(b"bG8=").unwrap();
50//! let decoded = decoder.finish().unwrap();
51//! assert_eq!(decoded, b"hello");
52//!
53//! let mut reader = DecoderReader::new(&b"aGVsbG8="[..], STANDARD);
54//! let mut decoded = Vec::new();
55//! reader.read_to_end(&mut decoded).unwrap();
56//! assert_eq!(decoded, b"hello");
57//! ```
58
59use super::{Alphabet, DecodeError, EncodeError, Engine};
60use std::io::{self, Read, Write};
61
62struct OutputQueue<const CAP: usize> {
63    buffer: [u8; CAP],
64    start: usize,
65    len: usize,
66}
67
68impl<const CAP: usize> OutputQueue<CAP> {
69    const fn new() -> Self {
70        Self {
71            buffer: [0; CAP],
72            start: 0,
73            len: 0,
74        }
75    }
76
77    const fn is_empty(&self) -> bool {
78        self.len == 0
79    }
80
81    const fn len(&self) -> usize {
82        self.len
83    }
84
85    const fn capacity(&self) -> usize {
86        self.len + self.available_capacity()
87    }
88
89    fn push_slice(&mut self, input: &[u8]) -> io::Result<()> {
90        if input.len() > self.available_capacity() {
91            return Err(io::Error::other(
92                "base64 stream output queue capacity exceeded",
93            ));
94        }
95
96        let mut read = 0;
97        while read < input.len() {
98            let write = (self.start + self.len) % CAP;
99            self.buffer[write] = input[read];
100            self.len += 1;
101            read += 1;
102        }
103
104        Ok(())
105    }
106
107    fn copy_front(&self, output: &mut [u8]) -> usize {
108        let count = core::cmp::min(self.len, output.len());
109        let first = core::cmp::min(count, CAP - self.start);
110        output[..first].copy_from_slice(&self.buffer[self.start..self.start + first]);
111
112        let second = count - first;
113        if second > 0 {
114            output[first..first + second].copy_from_slice(&self.buffer[..second]);
115        }
116
117        count
118    }
119
120    fn discard_front(&mut self, count: usize) {
121        let count = core::cmp::min(count, self.len);
122        let first = core::cmp::min(count, CAP - self.start);
123        crate::wipe_bytes(&mut self.buffer[self.start..self.start + first]);
124
125        let second = count - first;
126        if second > 0 {
127            crate::wipe_bytes(&mut self.buffer[..second]);
128        }
129
130        self.start = (self.start + count) % CAP;
131        self.len -= count;
132        if self.len == 0 {
133            self.start = 0;
134        }
135    }
136
137    fn pop_slice(&mut self, output: &mut [u8]) -> usize {
138        let count = self.copy_front(output);
139        self.discard_front(count);
140        count
141    }
142
143    fn clear_all(&mut self) {
144        crate::wipe_bytes(&mut self.buffer);
145        self.start = 0;
146        self.len = 0;
147    }
148
149    const fn available_capacity(&self) -> usize {
150        CAP - self.len
151    }
152}
153
154/// A streaming Base64 encoder for `std::io::Write`.
155///
156/// Like any [`Write`] implementation, [`Write::write`] may accept only
157/// part of the provided input. Accepted input may be held as encoded
158/// output until [`Write::flush`], [`Self::try_finish`], [`Self::finish`],
159/// or a later write drains the wrapped writer. Use [`Write::write_all`]
160/// when the whole input slice must be consumed.
161pub struct Encoder<W, A, const PAD: bool>
162where
163    A: Alphabet,
164{
165    inner: Option<W>,
166    engine: Engine<A, PAD>,
167    pending: [u8; 2],
168    pending_len: usize,
169    output: OutputQueue<1024>,
170    finalized: bool,
171    failed: bool,
172}
173
174impl<W, A, const PAD: bool> Encoder<W, A, PAD>
175where
176    A: Alphabet,
177{
178    /// Creates a new streaming encoder.
179    #[must_use]
180    pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
181        Self {
182            inner: Some(inner),
183            engine,
184            pending: [0; 2],
185            pending_len: 0,
186            output: OutputQueue::new(),
187            finalized: false,
188            failed: false,
189        }
190    }
191
192    /// Returns a shared reference to the wrapped writer.
193    #[must_use]
194    pub fn get_ref(&self) -> &W {
195        self.inner_ref()
196    }
197
198    /// Returns a mutable reference to the wrapped writer.
199    pub fn get_mut(&mut self) -> &mut W {
200        self.inner_mut()
201    }
202
203    /// Returns the Base64 engine used by this adapter.
204    #[must_use]
205    pub const fn engine(&self) -> Engine<A, PAD> {
206        self.engine
207    }
208
209    /// Returns whether this adapter uses padded Base64.
210    #[must_use]
211    pub const fn is_padded(&self) -> bool {
212        PAD
213    }
214
215    /// Returns the number of raw input bytes currently buffered until a
216    /// complete 3-byte Base64 encode quantum is available.
217    #[must_use]
218    pub const fn pending_len(&self) -> usize {
219        self.pending_len
220    }
221
222    /// Returns whether this encoder currently holds a partial input
223    /// quantum.
224    #[must_use]
225    pub const fn has_pending_input(&self) -> bool {
226        self.pending_len != 0
227    }
228
229    /// Returns how many additional input bytes are needed to complete the
230    /// currently buffered encode quantum.
231    ///
232    /// Returns `0` when no partial input quantum is buffered.
233    #[must_use]
234    pub const fn pending_input_needed_len(&self) -> usize {
235        if self.has_pending_input() {
236            3 - self.pending_len
237        } else {
238            0
239        }
240    }
241
242    /// Returns the number of encoded bytes buffered for the wrapped
243    /// writer after a previous write or flush could not fully drain them.
244    #[must_use]
245    pub const fn buffered_output_len(&self) -> usize {
246        self.output.len()
247    }
248
249    /// Returns the maximum number of encoded bytes this adapter can buffer
250    /// before returning bytes to the caller.
251    #[must_use]
252    pub const fn buffered_output_capacity(&self) -> usize {
253        self.output.capacity()
254    }
255
256    /// Returns how many more encoded bytes can be buffered before this
257    /// adapter must drain the wrapped writer.
258    #[must_use]
259    pub const fn buffered_output_remaining_capacity(&self) -> usize {
260        self.output.available_capacity()
261    }
262
263    /// Returns whether this encoder has encoded output waiting to be
264    /// written to the wrapped writer.
265    #[must_use]
266    pub const fn has_buffered_output(&self) -> bool {
267        !self.output.is_empty()
268    }
269
270    /// Returns whether this encoder has been finalized.
271    ///
272    /// Once this returns `true`, later writes return an error.
273    #[must_use]
274    pub const fn is_finalized(&self) -> bool {
275        self.finalized
276    }
277
278    /// Returns whether this encoder has failed closed after an unrecoverable
279    /// internal encoding or buffering error.
280    ///
281    /// Ordinary wrapped-writer I/O errors are retryable and do not set this
282    /// flag. Once this returns `true`, later writes, flushes, and finalization
283    /// attempts return an error. The unchecked [`Self::into_inner`] method can
284    /// still be used for explicit recovery of the wrapped writer.
285    #[must_use]
286    pub const fn is_failed(&self) -> bool {
287        self.failed
288    }
289
290    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
291    /// writer without discarding pending input.
292    #[must_use]
293    pub const fn can_into_inner(&self) -> bool {
294        !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
295    }
296
297    /// Consumes the encoder without flushing pending input.
298    ///
299    /// Prefer [`Self::finish`] when the encoded output must be complete.
300    #[must_use]
301    pub fn into_inner(mut self) -> W {
302        self.take_inner()
303    }
304
305    /// Consumes the encoder only when no partial input quantum is buffered.
306    ///
307    /// This does not flush or finalize the wrapped writer. It is a checked
308    /// alternative to [`Self::into_inner`] for callers that want to avoid
309    /// accidentally discarding pending input bytes.
310    #[allow(clippy::result_large_err)]
311    pub fn try_into_inner(mut self) -> Result<W, Self> {
312        if !self.can_into_inner() {
313            return Err(self);
314        }
315        Ok(self.take_inner())
316    }
317
318    fn inner_ref(&self) -> &W {
319        match &self.inner {
320            Some(inner) => inner,
321            None => unreachable!("stream encoder inner writer was already taken"),
322        }
323    }
324
325    fn inner_mut(&mut self) -> &mut W {
326        match &mut self.inner {
327            Some(inner) => inner,
328            None => unreachable!("stream encoder inner writer was already taken"),
329        }
330    }
331
332    fn take_inner(&mut self) -> W {
333        match self.inner.take() {
334            Some(inner) => inner,
335            None => unreachable!("stream encoder inner writer was already taken"),
336        }
337    }
338
339    fn clear_pending(&mut self) {
340        crate::wipe_bytes(&mut self.pending);
341        self.pending_len = 0;
342    }
343
344    fn clear_output(&mut self) {
345        self.output.clear_all();
346    }
347}
348
349impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
350where
351    A: Alphabet,
352{
353    fn drop(&mut self) {
354        self.clear_pending();
355        self.clear_output();
356    }
357}
358
359impl<W, A, const PAD: bool> core::fmt::Debug for Encoder<W, A, PAD>
360where
361    A: Alphabet,
362{
363    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364        formatter
365            .debug_struct("Encoder")
366            .field("inner", &redacted_inner_state(self.inner.is_some()))
367            .field("engine", &self.engine)
368            .field("pending", &"<redacted>")
369            .field("pending_len", &self.pending_len)
370            .field("pending_input_needed_len", &self.pending_input_needed_len())
371            .field("buffered_output_len", &self.output.len())
372            .field("buffered_output_capacity", &self.output.capacity())
373            .field(
374                "buffered_output_remaining_capacity",
375                &self.output.available_capacity(),
376            )
377            .field("can_into_inner", &self.can_into_inner())
378            .field("finalized", &self.finalized)
379            .field("failed", &self.failed)
380            .finish()
381    }
382}
383
384impl<W, A, const PAD: bool> Encoder<W, A, PAD>
385where
386    W: Write,
387    A: Alphabet,
388{
389    /// Writes any pending input and flushes the wrapped writer without
390    /// consuming this encoder.
391    ///
392    /// After this succeeds, [`Self::pending_len`] returns `0`, later
393    /// writes are rejected, and [`Self::finish`] can still be used to
394    /// recover the wrapped writer.
395    /// This is useful when a caller needs to finalize a framed payload
396    /// while keeping the stream adapter available for diagnostics or
397    /// explicit recovery.
398    pub fn try_finish(&mut self) -> io::Result<()> {
399        if self.failed {
400            return Err(stream_encoder_failed_error());
401        }
402        if !self.finalized {
403            self.queue_pending_final()?;
404            self.finalized = true;
405        }
406        self.flush()
407    }
408
409    /// Writes any pending input, flushes the wrapped writer, and returns it.
410    pub fn finish(mut self) -> io::Result<W> {
411        self.try_finish()?;
412        Ok(self.take_inner())
413    }
414
415    fn queue_pending_final(&mut self) -> io::Result<()> {
416        if self.pending_len == 0 {
417            return Ok(());
418        }
419
420        let mut pending = [0u8; 2];
421        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
422        let pending_len = self.pending_len;
423        let mut encoded = [0u8; 4];
424        let result = self.queue_encoded_temp(&pending[..pending_len], &mut encoded);
425        crate::wipe_bytes(&mut pending);
426        result?;
427        self.clear_pending();
428        Ok(())
429    }
430
431    fn queue_encoded_temp(&mut self, input: &[u8], encoded: &mut [u8]) -> io::Result<()> {
432        let written = match self.engine.encode_slice(input, encoded) {
433            Ok(written) => written,
434            Err(err) => {
435                crate::wipe_bytes(encoded);
436                self.failed = true;
437                return Err(encode_error_to_io(err));
438            }
439        };
440
441        let result = self.output.push_slice(&encoded[..written]);
442        crate::wipe_bytes(encoded);
443        if result.is_err() {
444            self.failed = true;
445        }
446        result
447    }
448
449    fn drain_output(&mut self) -> io::Result<()> {
450        let mut chunk = [0u8; 1024];
451        while !self.output.is_empty() {
452            let pending = self.output.copy_front(&mut chunk);
453            let result = self.inner_mut().write(&chunk[..pending]);
454            crate::wipe_bytes(&mut chunk[..pending]);
455            match result {
456                Ok(0) => {
457                    return Err(io::Error::new(
458                        io::ErrorKind::WriteZero,
459                        "base64 stream encoder could not drain buffered output",
460                    ));
461                }
462                Ok(written) => {
463                    if written > pending {
464                        self.failed = true;
465                        return Err(io::Error::new(
466                            io::ErrorKind::InvalidData,
467                            "wrapped writer reported more bytes than provided",
468                        ));
469                    }
470                    self.output.discard_front(written);
471                }
472                Err(err) => return Err(err),
473            }
474        }
475
476        Ok(())
477    }
478}
479
480impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
481where
482    W: Write,
483    A: Alphabet,
484{
485    fn write(&mut self, input: &[u8]) -> io::Result<usize> {
486        if self.failed {
487            return Err(stream_encoder_failed_error());
488        }
489        self.drain_output()?;
490        if self.finalized {
491            return Err(io::Error::new(
492                io::ErrorKind::InvalidInput,
493                "base64 stream encoder received input after finalization",
494            ));
495        }
496        if input.is_empty() {
497            return Ok(0);
498        }
499
500        let mut consumed = 0;
501        if self.pending_len > 0 {
502            let needed = 3 - self.pending_len;
503            if input.len() < needed {
504                self.pending[self.pending_len..self.pending_len + input.len()]
505                    .copy_from_slice(input);
506                self.pending_len += input.len();
507                return Ok(input.len());
508            }
509
510            let mut chunk = [0u8; 3];
511            chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
512            chunk[self.pending_len..].copy_from_slice(&input[..needed]);
513
514            let mut encoded = [0u8; 4];
515            let result = self.queue_encoded_temp(&chunk, &mut encoded);
516            crate::wipe_bytes(&mut chunk);
517            result?;
518            self.clear_pending();
519            consumed += needed;
520        }
521
522        let remaining = &input[consumed..];
523        let full_len = remaining.len() / 3 * 3;
524        if full_len > 0 {
525            let max_by_queue = self.output.available_capacity() / 4 * 3;
526            let mut take = core::cmp::min(full_len, core::cmp::min(768, max_by_queue));
527            take -= take % 3;
528
529            if take == 0 {
530                return Ok(consumed);
531            }
532
533            let mut encoded = [0u8; 1024];
534            self.queue_encoded_temp(&remaining[..take], &mut encoded)?;
535            consumed += take;
536
537            if take < full_len {
538                return Ok(consumed);
539            }
540        }
541
542        let tail = &input[consumed..];
543        self.pending[..tail.len()].copy_from_slice(tail);
544        self.pending_len = tail.len();
545        consumed += tail.len();
546
547        Ok(consumed)
548    }
549
550    fn flush(&mut self) -> io::Result<()> {
551        if self.failed {
552            return Err(stream_encoder_failed_error());
553        }
554        self.drain_output()?;
555        self.inner_mut().flush()
556    }
557}
558
559fn encode_error_to_io(err: EncodeError) -> io::Error {
560    io::Error::new(io::ErrorKind::InvalidInput, err)
561}
562
563/// A streaming Base64 decoder for `std::io::Write`.
564///
565/// Like any [`Write`] implementation, [`Write::write`] may accept only
566/// part of the provided input. Accepted input may be held as decoded
567/// output until [`Write::flush`], [`Self::try_finish`], [`Self::finish`],
568/// or a later write drains the wrapped writer. Use [`Write::write_all`]
569/// when the whole input slice must be consumed.
570///
571/// # Security
572///
573/// This adapter uses the normal strict decoder, not the [`crate::ct`]
574/// module. It may branch or return early based on malformed input and it
575/// preserves strict error diagnostics. Do not use it for secret-bearing
576/// payloads when malformed-input timing matters; decode a complete frame
577/// with the matching `ct` engine instead.
578pub struct Decoder<W, A, const PAD: bool>
579where
580    A: Alphabet,
581{
582    inner: Option<W>,
583    engine: Engine<A, PAD>,
584    pending: [u8; 4],
585    pending_len: usize,
586    output: OutputQueue<1024>,
587    finished: bool,
588    failed: bool,
589    finalized: bool,
590}
591
592impl<W, A, const PAD: bool> Decoder<W, A, PAD>
593where
594    A: Alphabet,
595{
596    /// Creates a new streaming decoder.
597    ///
598    /// # Security
599    ///
600    /// Streaming decoders use the normal strict decode path. They are not
601    /// constant-time-oriented secret decoders.
602    #[must_use]
603    pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
604        Self {
605            inner: Some(inner),
606            engine,
607            pending: [0; 4],
608            pending_len: 0,
609            output: OutputQueue::new(),
610            finished: false,
611            finalized: false,
612            failed: false,
613        }
614    }
615
616    /// Returns a shared reference to the wrapped writer.
617    #[must_use]
618    pub fn get_ref(&self) -> &W {
619        self.inner_ref()
620    }
621
622    /// Returns a mutable reference to the wrapped writer.
623    pub fn get_mut(&mut self) -> &mut W {
624        self.inner_mut()
625    }
626
627    /// Returns the Base64 engine used by this adapter.
628    #[must_use]
629    pub const fn engine(&self) -> Engine<A, PAD> {
630        self.engine
631    }
632
633    /// Returns whether this adapter uses padded Base64.
634    #[must_use]
635    pub const fn is_padded(&self) -> bool {
636        PAD
637    }
638
639    /// Returns the number of encoded input bytes currently buffered until
640    /// a complete 4-byte Base64 decode quantum is available.
641    #[must_use]
642    pub const fn pending_len(&self) -> usize {
643        self.pending_len
644    }
645
646    /// Returns whether this decoder currently holds a partial input
647    /// quantum.
648    #[must_use]
649    pub const fn has_pending_input(&self) -> bool {
650        self.pending_len != 0
651    }
652
653    /// Returns how many additional input bytes are needed to complete the
654    /// currently buffered decode quantum.
655    ///
656    /// Returns `0` when no partial input quantum is buffered.
657    #[must_use]
658    pub const fn pending_input_needed_len(&self) -> usize {
659        if self.has_pending_input() {
660            4 - self.pending_len
661        } else {
662            0
663        }
664    }
665
666    /// Returns the number of decoded bytes buffered for the wrapped writer
667    /// after a previous write or flush could not fully drain them.
668    #[must_use]
669    pub const fn buffered_output_len(&self) -> usize {
670        self.output.len()
671    }
672
673    /// Returns the maximum number of decoded bytes this adapter can buffer
674    /// before returning bytes to the caller.
675    #[must_use]
676    pub const fn buffered_output_capacity(&self) -> usize {
677        self.output.capacity()
678    }
679
680    /// Returns how many more decoded bytes can be buffered before this
681    /// adapter must drain the wrapped writer.
682    #[must_use]
683    pub const fn buffered_output_remaining_capacity(&self) -> usize {
684        self.output.available_capacity()
685    }
686
687    /// Returns whether this decoder has decoded output waiting to be
688    /// written to the wrapped writer.
689    #[must_use]
690    pub const fn has_buffered_output(&self) -> bool {
691        !self.output.is_empty()
692    }
693
694    /// Returns whether this decoder has processed a terminal padded block.
695    ///
696    /// Once this returns `true`, later calls to [`Write::write`] with
697    /// additional input return an error because strict Base64 does not
698    /// permit trailing payload bytes after padding.
699    #[must_use]
700    pub const fn has_terminal_padding(&self) -> bool {
701        self.finished
702    }
703
704    /// Returns whether this decoder has been finalized.
705    ///
706    /// Once this returns `true`, later non-empty writes return an error.
707    #[must_use]
708    pub const fn is_finalized(&self) -> bool {
709        self.finalized
710    }
711
712    /// Returns whether this decoder has rejected malformed Base64 input.
713    ///
714    /// Once this returns `true`, later writes, flushes, and finalization
715    /// attempts return an error. The unchecked [`Self::into_inner`] method
716    /// can still be used for explicit recovery of the wrapped writer.
717    #[must_use]
718    pub const fn is_failed(&self) -> bool {
719        self.failed
720    }
721
722    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
723    /// writer without discarding pending encoded input.
724    #[must_use]
725    pub const fn can_into_inner(&self) -> bool {
726        !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
727    }
728
729    /// Consumes the decoder without flushing pending input.
730    ///
731    /// Prefer [`Self::finish`] when the decoded output must be complete.
732    #[must_use]
733    pub fn into_inner(mut self) -> W {
734        self.take_inner()
735    }
736
737    /// Consumes the decoder only when no partial input quantum is buffered.
738    ///
739    /// This does not flush or finalize the wrapped writer. It is a checked
740    /// alternative to [`Self::into_inner`] for callers that want to avoid
741    /// accidentally discarding pending encoded input bytes.
742    #[allow(clippy::result_large_err)]
743    pub fn try_into_inner(mut self) -> Result<W, Self> {
744        if !self.can_into_inner() {
745            return Err(self);
746        }
747        Ok(self.take_inner())
748    }
749
750    fn inner_ref(&self) -> &W {
751        match &self.inner {
752            Some(inner) => inner,
753            None => unreachable!("stream decoder inner writer was already taken"),
754        }
755    }
756
757    fn inner_mut(&mut self) -> &mut W {
758        match &mut self.inner {
759            Some(inner) => inner,
760            None => unreachable!("stream decoder inner writer was already taken"),
761        }
762    }
763
764    fn take_inner(&mut self) -> W {
765        match self.inner.take() {
766            Some(inner) => inner,
767            None => unreachable!("stream decoder inner writer was already taken"),
768        }
769    }
770
771    fn clear_pending(&mut self) {
772        crate::wipe_bytes(&mut self.pending);
773        self.pending_len = 0;
774    }
775
776    fn clear_output(&mut self) {
777        self.output.clear_all();
778    }
779}
780
781impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
782where
783    A: Alphabet,
784{
785    fn drop(&mut self) {
786        self.clear_pending();
787        self.clear_output();
788    }
789}
790
791impl<W, A, const PAD: bool> core::fmt::Debug for Decoder<W, A, PAD>
792where
793    A: Alphabet,
794{
795    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
796        formatter
797            .debug_struct("Decoder")
798            .field("inner", &redacted_inner_state(self.inner.is_some()))
799            .field("engine", &self.engine)
800            .field("pending", &"<redacted>")
801            .field("pending_len", &self.pending_len)
802            .field("pending_input_needed_len", &self.pending_input_needed_len())
803            .field("buffered_output_len", &self.output.len())
804            .field("buffered_output_capacity", &self.output.capacity())
805            .field(
806                "buffered_output_remaining_capacity",
807                &self.output.available_capacity(),
808            )
809            .field("can_into_inner", &self.can_into_inner())
810            .field("terminal_padding", &self.finished)
811            .field("finalized", &self.finalized)
812            .field("failed", &self.failed)
813            .finish()
814    }
815}
816
817impl<W, A, const PAD: bool> Decoder<W, A, PAD>
818where
819    W: Write,
820    A: Alphabet,
821{
822    /// Validates any final pending input and flushes the wrapped writer
823    /// without consuming this decoder.
824    ///
825    /// After this succeeds, [`Self::pending_len`] returns `0`, later
826    /// writes are rejected, and [`Self::finish`] can still be used to
827    /// recover the wrapped writer.
828    /// If the final buffered input is malformed, an error is returned and
829    /// the caller still owns the decoder for diagnostics or explicit
830    /// recovery.
831    pub fn try_finish(&mut self) -> io::Result<()> {
832        if self.failed {
833            return Err(stream_decoder_failed_error());
834        }
835        if !self.finalized {
836            self.queue_pending_final()?;
837            self.finalized = true;
838        }
839        self.flush()
840    }
841
842    /// Validates final pending input, flushes the wrapped writer, and returns it.
843    pub fn finish(mut self) -> io::Result<W> {
844        self.try_finish()?;
845        Ok(self.take_inner())
846    }
847
848    fn queue_pending_final(&mut self) -> io::Result<()> {
849        if self.pending_len == 0 {
850            return Ok(());
851        }
852
853        let mut pending = [0u8; 4];
854        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
855        let pending_len = self.pending_len;
856        let mut decoded = [0u8; 3];
857        let result = self.queue_decoded_temp(&pending[..pending_len], &mut decoded);
858        crate::wipe_bytes(&mut pending);
859        if let Err(err) = result {
860            self.clear_pending();
861            return Err(err);
862        }
863        self.clear_pending();
864        Ok(())
865    }
866
867    fn queue_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
868        let mut decoded = [0u8; 3];
869        let result = self.queue_decoded_temp(&input, &mut decoded);
870        crate::wipe_bytes(&mut input);
871        let written = result?;
872        if written < 3 {
873            self.finished = true;
874        }
875        Ok(())
876    }
877
878    fn queue_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
879        let written = match self.engine.decode_slice(input, decoded) {
880            Ok(written) => written,
881            Err(err) => {
882                crate::wipe_bytes(decoded);
883                self.failed = true;
884                return Err(decode_error_to_io(err));
885            }
886        };
887
888        let result = self.output.push_slice(&decoded[..written]);
889        crate::wipe_bytes(decoded);
890        result?;
891        Ok(written)
892    }
893
894    fn drain_output(&mut self) -> io::Result<()> {
895        let mut chunk = [0u8; 1024];
896        while !self.output.is_empty() {
897            let pending = self.output.copy_front(&mut chunk);
898            let result = self.inner_mut().write(&chunk[..pending]);
899            crate::wipe_bytes(&mut chunk[..pending]);
900            match result {
901                Ok(0) => {
902                    return Err(io::Error::new(
903                        io::ErrorKind::WriteZero,
904                        "base64 stream decoder could not drain buffered output",
905                    ));
906                }
907                Ok(written) => {
908                    if written > pending {
909                        self.failed = true;
910                        return Err(io::Error::new(
911                            io::ErrorKind::InvalidData,
912                            "wrapped writer reported more bytes than provided",
913                        ));
914                    }
915                    self.output.discard_front(written);
916                }
917                Err(err) => return Err(err),
918            }
919        }
920
921        Ok(())
922    }
923}
924
925impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
926where
927    W: Write,
928    A: Alphabet,
929{
930    fn write(&mut self, input: &[u8]) -> io::Result<usize> {
931        if self.failed {
932            return Err(stream_decoder_failed_error());
933        }
934        if input.is_empty() {
935            self.drain_output()?;
936            return Ok(0);
937        }
938        self.drain_output()?;
939        if self.finalized {
940            return Err(io::Error::new(
941                io::ErrorKind::InvalidInput,
942                "base64 stream decoder received input after finalization",
943            ));
944        }
945        if self.finished {
946            self.failed = true;
947            return Err(trailing_input_after_padding_error());
948        }
949
950        let mut consumed = 0;
951        if self.pending_len > 0 {
952            let needed = 4 - self.pending_len;
953            if input.len() < needed {
954                self.pending[self.pending_len..self.pending_len + input.len()]
955                    .copy_from_slice(input);
956                self.pending_len += input.len();
957                return Ok(input.len());
958            }
959
960            let mut quad = [0u8; 4];
961            quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
962            quad[self.pending_len..].copy_from_slice(&input[..needed]);
963            let result = self.queue_full_quad(quad);
964            crate::wipe_bytes(&mut quad);
965            if let Err(err) = result {
966                self.clear_pending();
967                return Err(err);
968            }
969            self.clear_pending();
970            consumed += needed;
971
972            if self.finished {
973                return Ok(consumed);
974            }
975        }
976
977        while input.len() - consumed >= 4 {
978            if self.output.available_capacity() < 3 {
979                return Ok(consumed);
980            }
981
982            let mut quad = [
983                input[consumed],
984                input[consumed + 1],
985                input[consumed + 2],
986                input[consumed + 3],
987            ];
988            let mut decoded = [0u8; 3];
989            let written = match self.engine.decode_slice(&quad, &mut decoded) {
990                Ok(written) => written,
991                Err(err) => {
992                    crate::wipe_bytes(&mut quad);
993                    crate::wipe_bytes(&mut decoded);
994                    self.failed = true;
995                    if consumed > 0 {
996                        return Ok(consumed);
997                    }
998
999                    return Err(decode_error_to_io(err));
1000                }
1001            };
1002
1003            let result = self.output.push_slice(&decoded[..written]);
1004            crate::wipe_bytes(&mut quad);
1005            crate::wipe_bytes(&mut decoded);
1006            result?;
1007            consumed += 4;
1008
1009            if written < 3 {
1010                self.finished = true;
1011                return Ok(consumed);
1012            }
1013        }
1014
1015        let tail = &input[consumed..];
1016        self.pending[..tail.len()].copy_from_slice(tail);
1017        self.pending_len = tail.len();
1018        consumed += tail.len();
1019
1020        Ok(consumed)
1021    }
1022
1023    fn flush(&mut self) -> io::Result<()> {
1024        if self.failed {
1025            return Err(stream_decoder_failed_error());
1026        }
1027        self.drain_output()?;
1028        self.inner_mut().flush()
1029    }
1030}
1031
1032fn decode_error_to_io(err: DecodeError) -> io::Error {
1033    io::Error::new(io::ErrorKind::InvalidInput, err)
1034}
1035
1036fn trailing_input_after_padding_error() -> io::Error {
1037    io::Error::new(
1038        io::ErrorKind::InvalidInput,
1039        "base64 decoder received trailing input after padding",
1040    )
1041}
1042
1043fn stream_decoder_failed_error() -> io::Error {
1044    io::Error::new(
1045        io::ErrorKind::InvalidInput,
1046        "base64 stream decoder is failed after malformed input",
1047    )
1048}
1049
1050fn stream_encoder_failed_error() -> io::Error {
1051    io::Error::new(
1052        io::ErrorKind::InvalidInput,
1053        "base64 stream encoder is failed after internal error",
1054    )
1055}
1056
1057/// A streaming Base64 decoder for `std::io::Read`.
1058///
1059/// For padded engines, this reader stops at the terminal padded Base64
1060/// block and leaves later bytes unread in the wrapped reader. This preserves
1061/// boundaries for callers that decode one Base64 payload from a larger
1062/// stream.
1063///
1064/// # Security
1065///
1066/// This adapter uses the normal strict decoder, not the [`crate::ct`]
1067/// module. It may branch or return early based on malformed input and it
1068/// preserves strict error diagnostics. Do not use it for secret-bearing
1069/// payloads when malformed-input timing matters; decode a complete frame
1070/// with the matching `ct` engine instead.
1071pub struct DecoderReader<R, A, const PAD: bool>
1072where
1073    A: Alphabet,
1074{
1075    inner: Option<R>,
1076    engine: Engine<A, PAD>,
1077    pending: [u8; 4],
1078    pending_len: usize,
1079    output: OutputQueue<3>,
1080    finished: bool,
1081    terminal_seen: bool,
1082    failed: bool,
1083}
1084
1085impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1086where
1087    A: Alphabet,
1088{
1089    /// Creates a new streaming decoder reader.
1090    ///
1091    /// # Security
1092    ///
1093    /// Streaming decoder readers use the normal strict decode path. They
1094    /// are not constant-time-oriented secret decoders.
1095    #[must_use]
1096    pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1097        Self {
1098            inner: Some(inner),
1099            engine,
1100            pending: [0; 4],
1101            pending_len: 0,
1102            output: OutputQueue::new(),
1103            finished: false,
1104            terminal_seen: false,
1105            failed: false,
1106        }
1107    }
1108
1109    /// Returns a shared reference to the wrapped reader.
1110    #[must_use]
1111    pub fn get_ref(&self) -> &R {
1112        self.inner_ref()
1113    }
1114
1115    /// Returns a mutable reference to the wrapped reader.
1116    pub fn get_mut(&mut self) -> &mut R {
1117        self.inner_mut()
1118    }
1119
1120    /// Returns the Base64 engine used by this adapter.
1121    #[must_use]
1122    pub const fn engine(&self) -> Engine<A, PAD> {
1123        self.engine
1124    }
1125
1126    /// Returns whether this adapter uses padded Base64.
1127    #[must_use]
1128    pub const fn is_padded(&self) -> bool {
1129        PAD
1130    }
1131
1132    /// Returns the number of encoded input bytes currently buffered until
1133    /// a complete 4-byte Base64 decode quantum is available.
1134    #[must_use]
1135    pub const fn pending_len(&self) -> usize {
1136        self.pending_len
1137    }
1138
1139    /// Returns whether this decoder reader currently holds a partial input
1140    /// quantum.
1141    #[must_use]
1142    pub const fn has_pending_input(&self) -> bool {
1143        self.pending_len != 0
1144    }
1145
1146    /// Returns how many additional encoded input bytes are needed to
1147    /// complete the currently buffered decode quantum.
1148    ///
1149    /// Returns `0` when no partial input quantum is buffered.
1150    #[must_use]
1151    pub const fn pending_input_needed_len(&self) -> usize {
1152        if self.has_pending_input() {
1153            4 - self.pending_len
1154        } else {
1155            0
1156        }
1157    }
1158
1159    /// Returns the number of decoded bytes currently buffered and ready to
1160    /// be read before this adapter polls the wrapped reader again.
1161    #[must_use]
1162    pub const fn buffered_output_len(&self) -> usize {
1163        self.output.len()
1164    }
1165
1166    /// Returns the maximum number of decoded bytes this adapter can buffer
1167    /// before returning bytes to the caller.
1168    #[must_use]
1169    pub const fn buffered_output_capacity(&self) -> usize {
1170        self.output.capacity()
1171    }
1172
1173    /// Returns how many more decoded bytes can be buffered before this
1174    /// adapter must return bytes to the caller.
1175    #[must_use]
1176    pub const fn buffered_output_remaining_capacity(&self) -> usize {
1177        self.output.available_capacity()
1178    }
1179
1180    /// Returns whether this decoder reader currently has decoded output
1181    /// waiting in its internal queue.
1182    #[must_use]
1183    pub const fn has_buffered_output(&self) -> bool {
1184        !self.output.is_empty()
1185    }
1186
1187    /// Returns whether this decoder reader has seen terminal padding.
1188    ///
1189    /// For padded engines, this becomes `true` after the terminal padded
1190    /// block is decoded. The wrapped reader is then left positioned after
1191    /// that Base64 block so adjacent framed bytes can be read by the
1192    /// caller.
1193    #[must_use]
1194    pub const fn has_terminal_padding(&self) -> bool {
1195        self.terminal_seen
1196    }
1197
1198    /// Returns whether this decoder reader has reached EOF or terminal
1199    /// padding in the wrapped reader.
1200    ///
1201    /// This may become `true` before [`Self::is_finished`] when decoded
1202    /// output is still buffered for the caller.
1203    #[must_use]
1204    pub const fn has_finished_input(&self) -> bool {
1205        self.finished
1206    }
1207
1208    /// Returns whether this reader has reached EOF or terminal padding
1209    /// and has no decoded output buffered for the caller.
1210    #[must_use]
1211    pub const fn is_finished(&self) -> bool {
1212        self.finished && self.output.is_empty()
1213    }
1214
1215    /// Returns whether this decoder reader has rejected malformed Base64
1216    /// input.
1217    ///
1218    /// Once this returns `true`, later reads return an error. The unchecked
1219    /// [`Self::into_inner`] method can still be used for explicit recovery
1220    /// of the wrapped reader.
1221    #[must_use]
1222    pub const fn is_failed(&self) -> bool {
1223        self.failed
1224    }
1225
1226    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
1227    /// reader without discarding buffered decoded output.
1228    #[must_use]
1229    pub const fn can_into_inner(&self) -> bool {
1230        !self.is_failed() && self.is_finished()
1231    }
1232
1233    /// Consumes the decoder reader and returns the wrapped reader.
1234    #[must_use]
1235    pub fn into_inner(mut self) -> R {
1236        self.take_inner()
1237    }
1238
1239    /// Consumes the decoder reader only after the Base64 payload is fully
1240    /// drained.
1241    ///
1242    /// For padded streams, terminal padding may leave adjacent framed bytes
1243    /// unread in the wrapped reader. This method succeeds only after all
1244    /// decoded output buffered by this adapter has been read, so recovering
1245    /// the wrapped reader does not silently discard decoded bytes.
1246    #[allow(clippy::result_large_err)]
1247    pub fn try_into_inner(mut self) -> Result<R, Self> {
1248        if !self.can_into_inner() {
1249            return Err(self);
1250        }
1251        Ok(self.take_inner())
1252    }
1253
1254    fn inner_ref(&self) -> &R {
1255        match &self.inner {
1256            Some(inner) => inner,
1257            None => unreachable!("stream decoder reader inner reader was already taken"),
1258        }
1259    }
1260
1261    fn inner_mut(&mut self) -> &mut R {
1262        match &mut self.inner {
1263            Some(inner) => inner,
1264            None => unreachable!("stream decoder reader inner reader was already taken"),
1265        }
1266    }
1267
1268    fn take_inner(&mut self) -> R {
1269        match self.inner.take() {
1270            Some(inner) => inner,
1271            None => unreachable!("stream decoder reader inner reader was already taken"),
1272        }
1273    }
1274
1275    fn clear_pending(&mut self) {
1276        crate::wipe_bytes(&mut self.pending);
1277        self.pending_len = 0;
1278    }
1279}
1280
1281impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
1282where
1283    A: Alphabet,
1284{
1285    fn drop(&mut self) {
1286        self.clear_pending();
1287        self.output.clear_all();
1288    }
1289}
1290
1291impl<R, A, const PAD: bool> core::fmt::Debug for DecoderReader<R, A, PAD>
1292where
1293    A: Alphabet,
1294{
1295    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1296        formatter
1297            .debug_struct("DecoderReader")
1298            .field("inner", &redacted_inner_state(self.inner.is_some()))
1299            .field("engine", &self.engine)
1300            .field("pending", &"<redacted>")
1301            .field("pending_len", &self.pending_len)
1302            .field("pending_input_needed_len", &self.pending_input_needed_len())
1303            .field("buffered_output_len", &self.output.len())
1304            .field("buffered_output_capacity", &self.output.capacity())
1305            .field(
1306                "buffered_output_remaining_capacity",
1307                &self.output.available_capacity(),
1308            )
1309            .field("can_into_inner", &self.can_into_inner())
1310            .field("finished", &self.finished)
1311            .field("terminal_padding", &self.terminal_seen)
1312            .field("failed", &self.failed)
1313            .finish()
1314    }
1315}
1316
1317impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
1318where
1319    R: Read,
1320    A: Alphabet,
1321{
1322    fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1323        if output.is_empty() {
1324            return Ok(0);
1325        }
1326        if self.failed {
1327            return Err(stream_decoder_failed_error());
1328        }
1329
1330        while self.output.is_empty() && !self.finished {
1331            self.fill_output()?;
1332        }
1333
1334        Ok(self.output.pop_slice(output))
1335    }
1336}
1337
1338impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1339where
1340    R: Read,
1341    A: Alphabet,
1342{
1343    fn fill_output(&mut self) -> io::Result<()> {
1344        if self.failed {
1345            return Err(stream_decoder_failed_error());
1346        }
1347        if self.terminal_seen {
1348            self.finished = true;
1349            return Ok(());
1350        }
1351
1352        let mut input = [0u8; 4];
1353        let available = 4 - self.pending_len;
1354        let read = match self.inner_mut().read(&mut input[..available]) {
1355            Ok(read) => read,
1356            Err(err) => {
1357                crate::wipe_bytes(&mut input);
1358                return Err(err);
1359            }
1360        };
1361        if read == 0 {
1362            crate::wipe_bytes(&mut input);
1363            self.finished = true;
1364            self.push_final_pending()?;
1365            return Ok(());
1366        }
1367
1368        self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
1369        crate::wipe_bytes(&mut input);
1370        self.pending_len += read;
1371        if self.pending_len < 4 {
1372            return Ok(());
1373        }
1374
1375        let mut quad = self.pending;
1376        self.clear_pending();
1377        let result = self.push_decoded(&quad);
1378        crate::wipe_bytes(&mut quad);
1379        result?;
1380        if self.terminal_seen {
1381            self.finished = true;
1382        }
1383        Ok(())
1384    }
1385
1386    fn push_final_pending(&mut self) -> io::Result<()> {
1387        if self.pending_len == 0 {
1388            return Ok(());
1389        }
1390
1391        let mut pending = [0u8; 4];
1392        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1393        let pending_len = self.pending_len;
1394        self.clear_pending();
1395        let result = self.push_decoded(&pending[..pending_len]);
1396        crate::wipe_bytes(&mut pending);
1397        result
1398    }
1399
1400    fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
1401        let mut decoded = [0u8; 3];
1402        let written = match self.engine.decode_slice(input, &mut decoded) {
1403            Ok(written) => written,
1404            Err(err) => {
1405                crate::wipe_bytes(&mut decoded);
1406                self.failed = true;
1407                return Err(decode_error_to_io(err));
1408            }
1409        };
1410        let result = self.output.push_slice(&decoded[..written]);
1411        crate::wipe_bytes(&mut decoded);
1412        result?;
1413        if input.len() == 4 && written < 3 {
1414            self.terminal_seen = true;
1415        }
1416        Ok(())
1417    }
1418}
1419
1420/// A streaming Base64 encoder for `std::io::Read`.
1421pub struct EncoderReader<R, A, const PAD: bool>
1422where
1423    A: Alphabet,
1424{
1425    inner: Option<R>,
1426    engine: Engine<A, PAD>,
1427    pending: [u8; 2],
1428    pending_len: usize,
1429    output: OutputQueue<1024>,
1430    finished: bool,
1431    failed: bool,
1432}
1433
1434impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1435where
1436    A: Alphabet,
1437{
1438    /// Creates a new streaming encoder reader.
1439    #[must_use]
1440    pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1441        Self {
1442            inner: Some(inner),
1443            engine,
1444            pending: [0; 2],
1445            pending_len: 0,
1446            output: OutputQueue::new(),
1447            finished: false,
1448            failed: false,
1449        }
1450    }
1451
1452    /// Returns a shared reference to the wrapped reader.
1453    #[must_use]
1454    pub fn get_ref(&self) -> &R {
1455        self.inner_ref()
1456    }
1457
1458    /// Returns a mutable reference to the wrapped reader.
1459    pub fn get_mut(&mut self) -> &mut R {
1460        self.inner_mut()
1461    }
1462
1463    /// Returns the Base64 engine used by this adapter.
1464    #[must_use]
1465    pub const fn engine(&self) -> Engine<A, PAD> {
1466        self.engine
1467    }
1468
1469    /// Returns whether this adapter uses padded Base64.
1470    #[must_use]
1471    pub const fn is_padded(&self) -> bool {
1472        PAD
1473    }
1474
1475    /// Returns the number of raw input bytes currently buffered until a
1476    /// complete 3-byte Base64 encode quantum is available.
1477    #[must_use]
1478    pub const fn pending_len(&self) -> usize {
1479        self.pending_len
1480    }
1481
1482    /// Returns whether this encoder reader currently holds a partial input
1483    /// quantum.
1484    #[must_use]
1485    pub const fn has_pending_input(&self) -> bool {
1486        self.pending_len != 0
1487    }
1488
1489    /// Returns how many additional raw input bytes are needed to complete
1490    /// the currently buffered encode quantum.
1491    ///
1492    /// Returns `0` when no partial input quantum is buffered.
1493    #[must_use]
1494    pub const fn pending_input_needed_len(&self) -> usize {
1495        if self.has_pending_input() {
1496            3 - self.pending_len
1497        } else {
1498            0
1499        }
1500    }
1501
1502    /// Returns the number of encoded bytes currently buffered and ready to
1503    /// be read before this adapter polls the wrapped reader again.
1504    #[must_use]
1505    pub const fn buffered_output_len(&self) -> usize {
1506        self.output.len()
1507    }
1508
1509    /// Returns the maximum number of encoded bytes this adapter can buffer
1510    /// before returning bytes to the caller.
1511    #[must_use]
1512    pub const fn buffered_output_capacity(&self) -> usize {
1513        self.output.capacity()
1514    }
1515
1516    /// Returns how many more encoded bytes can be buffered before this
1517    /// adapter must return bytes to the caller.
1518    #[must_use]
1519    pub const fn buffered_output_remaining_capacity(&self) -> usize {
1520        self.output.available_capacity()
1521    }
1522
1523    /// Returns whether this encoder reader currently has encoded output
1524    /// waiting in its internal queue.
1525    #[must_use]
1526    pub const fn has_buffered_output(&self) -> bool {
1527        !self.output.is_empty()
1528    }
1529
1530    /// Returns whether this encoder reader has reached EOF in the wrapped
1531    /// reader.
1532    ///
1533    /// This may become `true` before [`Self::is_finished`] when encoded
1534    /// output is still buffered for the caller.
1535    #[must_use]
1536    pub const fn has_finished_input(&self) -> bool {
1537        self.finished
1538    }
1539
1540    /// Returns whether this reader has reached EOF and has no encoded
1541    /// output buffered for the caller.
1542    #[must_use]
1543    pub const fn is_finished(&self) -> bool {
1544        self.finished && self.output.is_empty()
1545    }
1546
1547    /// Returns whether this adapter has failed closed after an internal
1548    /// stream error.
1549    #[must_use]
1550    pub const fn is_failed(&self) -> bool {
1551        self.failed
1552    }
1553
1554    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
1555    /// reader without discarding pending input or buffered encoded output.
1556    #[must_use]
1557    pub const fn can_into_inner(&self) -> bool {
1558        self.is_finished() && !self.failed
1559    }
1560
1561    /// Consumes the encoder reader and returns the wrapped reader.
1562    #[must_use]
1563    pub fn into_inner(mut self) -> R {
1564        self.take_inner()
1565    }
1566
1567    /// Consumes the encoder reader only after the encoded stream is fully
1568    /// drained.
1569    ///
1570    /// This is a checked alternative to [`Self::into_inner`] for callers
1571    /// that want to avoid accidentally discarding pending input or encoded
1572    /// output buffered inside the adapter.
1573    #[allow(clippy::result_large_err)]
1574    pub fn try_into_inner(mut self) -> Result<R, Self> {
1575        if !self.can_into_inner() {
1576            return Err(self);
1577        }
1578        Ok(self.take_inner())
1579    }
1580
1581    fn inner_ref(&self) -> &R {
1582        match &self.inner {
1583            Some(inner) => inner,
1584            None => unreachable!("stream encoder reader inner reader was already taken"),
1585        }
1586    }
1587
1588    fn inner_mut(&mut self) -> &mut R {
1589        match &mut self.inner {
1590            Some(inner) => inner,
1591            None => unreachable!("stream encoder reader inner reader was already taken"),
1592        }
1593    }
1594
1595    fn take_inner(&mut self) -> R {
1596        match self.inner.take() {
1597            Some(inner) => inner,
1598            None => unreachable!("stream encoder reader inner reader was already taken"),
1599        }
1600    }
1601
1602    fn clear_pending(&mut self) {
1603        crate::wipe_bytes(&mut self.pending);
1604        self.pending_len = 0;
1605    }
1606}
1607
1608impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
1609where
1610    A: Alphabet,
1611{
1612    fn drop(&mut self) {
1613        self.clear_pending();
1614        self.output.clear_all();
1615    }
1616}
1617
1618impl<R, A, const PAD: bool> core::fmt::Debug for EncoderReader<R, A, PAD>
1619where
1620    A: Alphabet,
1621{
1622    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1623        formatter
1624            .debug_struct("EncoderReader")
1625            .field("inner", &redacted_inner_state(self.inner.is_some()))
1626            .field("engine", &self.engine)
1627            .field("pending", &"<redacted>")
1628            .field("pending_len", &self.pending_len)
1629            .field("pending_input_needed_len", &self.pending_input_needed_len())
1630            .field("buffered_output_len", &self.output.len())
1631            .field("buffered_output_capacity", &self.output.capacity())
1632            .field(
1633                "buffered_output_remaining_capacity",
1634                &self.output.available_capacity(),
1635            )
1636            .field("can_into_inner", &self.can_into_inner())
1637            .field("finished", &self.finished)
1638            .field("failed", &self.failed)
1639            .finish()
1640    }
1641}
1642
1643impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
1644where
1645    R: Read,
1646    A: Alphabet,
1647{
1648    fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1649        if self.failed {
1650            return Err(stream_encoder_failed_error());
1651        }
1652
1653        if output.is_empty() {
1654            return Ok(0);
1655        }
1656
1657        while self.output.is_empty() && !self.finished {
1658            self.fill_output()?;
1659        }
1660
1661        Ok(self.output.pop_slice(output))
1662    }
1663}
1664
1665impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1666where
1667    R: Read,
1668    A: Alphabet,
1669{
1670    fn fill_output(&mut self) -> io::Result<()> {
1671        let mut input = [0u8; 768];
1672        let read = match self.inner_mut().read(&mut input) {
1673            Ok(read) => read,
1674            Err(err) => {
1675                crate::wipe_bytes(&mut input);
1676                return Err(err);
1677            }
1678        };
1679        if read == 0 {
1680            crate::wipe_bytes(&mut input);
1681            self.finished = true;
1682            if let Err(err) = self.push_final_pending() {
1683                self.failed = true;
1684                return Err(err);
1685            }
1686            return Ok(());
1687        }
1688
1689        let mut consumed = 0;
1690        if self.pending_len > 0 {
1691            let needed = 3 - self.pending_len;
1692            if read < needed {
1693                self.pending[self.pending_len..self.pending_len + read]
1694                    .copy_from_slice(&input[..read]);
1695                self.pending_len += read;
1696                crate::wipe_bytes(&mut input);
1697                return Ok(());
1698            }
1699
1700            let mut chunk = [0u8; 3];
1701            chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1702            chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1703            let result = self.push_encoded(&chunk);
1704            crate::wipe_bytes(&mut chunk);
1705            if let Err(err) = result {
1706                crate::wipe_bytes(&mut input);
1707                self.failed = true;
1708                return Err(err);
1709            }
1710            self.clear_pending();
1711            consumed += needed;
1712        }
1713
1714        let remaining = &input[consumed..read];
1715        let full_len = remaining.len() / 3 * 3;
1716        let tail_len = remaining.len() - full_len;
1717        let mut tail = [0u8; 2];
1718        tail[..tail_len].copy_from_slice(&remaining[full_len..]);
1719        let result = if full_len > 0 {
1720            self.push_encoded(&remaining[..full_len])
1721        } else {
1722            Ok(())
1723        };
1724        crate::wipe_bytes(&mut input);
1725        if let Err(err) = result {
1726            crate::wipe_bytes(&mut tail);
1727            self.failed = true;
1728            return Err(err);
1729        }
1730        self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
1731        crate::wipe_bytes(&mut tail);
1732        self.pending_len = tail_len;
1733        Ok(())
1734    }
1735
1736    fn push_final_pending(&mut self) -> io::Result<()> {
1737        if self.pending_len == 0 {
1738            return Ok(());
1739        }
1740
1741        let mut pending = [0u8; 2];
1742        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1743        let pending_len = self.pending_len;
1744        self.clear_pending();
1745        let result = self.push_encoded(&pending[..pending_len]);
1746        crate::wipe_bytes(&mut pending);
1747        result
1748    }
1749
1750    fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
1751        let mut encoded = [0u8; 1024];
1752        let written = match self.engine.encode_slice(input, &mut encoded) {
1753            Ok(written) => written,
1754            Err(err) => {
1755                crate::wipe_bytes(&mut encoded);
1756                return Err(encode_error_to_io(err));
1757            }
1758        };
1759        let result = self.output.push_slice(&encoded[..written]);
1760        crate::wipe_bytes(&mut encoded);
1761        result
1762    }
1763}
1764
1765const fn redacted_inner_state(present: bool) -> &'static str {
1766    if present { "<present>" } else { "<taken>" }
1767}