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