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        result?;
900        Ok(written)
901    }
902
903    fn drain_output(&mut self) -> io::Result<()> {
904        let mut chunk = [0u8; 1024];
905        while !self.output.is_empty() {
906            let pending = self.output.copy_front(&mut chunk);
907            let result = self.inner_mut().write(&chunk[..pending]);
908            crate::wipe_bytes(&mut chunk[..pending]);
909            match result {
910                Ok(0) => {
911                    return Err(io::Error::new(
912                        io::ErrorKind::WriteZero,
913                        "base64 stream decoder could not drain buffered output",
914                    ));
915                }
916                Ok(written) => {
917                    if written > pending {
918                        self.failed = true;
919                        return Err(io::Error::new(
920                            io::ErrorKind::InvalidData,
921                            "wrapped writer reported more bytes than provided",
922                        ));
923                    }
924                    self.output.discard_front(written);
925                }
926                Err(err) => return Err(err),
927            }
928        }
929
930        Ok(())
931    }
932}
933
934impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
935where
936    W: Write,
937    A: Alphabet,
938{
939    fn write(&mut self, input: &[u8]) -> io::Result<usize> {
940        if self.failed {
941            return Err(stream_decoder_failed_error());
942        }
943        if input.is_empty() {
944            self.drain_output()?;
945            return Ok(0);
946        }
947        self.drain_output()?;
948        if self.finalized {
949            return Err(io::Error::new(
950                io::ErrorKind::InvalidInput,
951                "base64 stream decoder received input after finalization",
952            ));
953        }
954        if self.finished {
955            self.failed = true;
956            return Err(trailing_input_after_padding_error());
957        }
958
959        let mut consumed = 0;
960        if self.pending_len > 0 {
961            let needed = 4 - self.pending_len;
962            if input.len() < needed {
963                self.pending[self.pending_len..self.pending_len + input.len()]
964                    .copy_from_slice(input);
965                self.pending_len += input.len();
966                return Ok(input.len());
967            }
968
969            let mut quad = [0u8; 4];
970            quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
971            quad[self.pending_len..].copy_from_slice(&input[..needed]);
972            let result = self.queue_full_quad(quad);
973            crate::wipe_bytes(&mut quad);
974            if let Err(err) = result {
975                self.clear_pending();
976                return Err(err);
977            }
978            self.clear_pending();
979            consumed += needed;
980
981            if self.finished {
982                return Ok(consumed);
983            }
984        }
985
986        while input.len() - consumed >= 4 {
987            if self.output.available_capacity() < 3 {
988                return Ok(consumed);
989            }
990
991            let mut quad = [
992                input[consumed],
993                input[consumed + 1],
994                input[consumed + 2],
995                input[consumed + 3],
996            ];
997            let mut decoded = [0u8; 3];
998            let written = match self.engine.decode_slice(&quad, &mut decoded) {
999                Ok(written) => written,
1000                Err(err) => {
1001                    crate::wipe_bytes(&mut quad);
1002                    crate::wipe_bytes(&mut decoded);
1003                    self.failed = true;
1004                    if consumed > 0 {
1005                        return Ok(consumed);
1006                    }
1007
1008                    return Err(decode_error_to_io(err));
1009                }
1010            };
1011
1012            let result = self.output.push_slice(&decoded[..written]);
1013            crate::wipe_bytes(&mut quad);
1014            crate::wipe_bytes(&mut decoded);
1015            result?;
1016            consumed += 4;
1017
1018            if written < 3 {
1019                self.finished = true;
1020                return Ok(consumed);
1021            }
1022        }
1023
1024        let tail = &input[consumed..];
1025        self.pending[..tail.len()].copy_from_slice(tail);
1026        self.pending_len = tail.len();
1027        consumed += tail.len();
1028
1029        Ok(consumed)
1030    }
1031
1032    fn flush(&mut self) -> io::Result<()> {
1033        if self.failed {
1034            return Err(stream_decoder_failed_error());
1035        }
1036        self.drain_output()?;
1037        self.inner_mut().flush()
1038    }
1039}
1040
1041fn decode_error_to_io(err: DecodeError) -> io::Error {
1042    io::Error::new(io::ErrorKind::InvalidInput, err)
1043}
1044
1045fn trailing_input_after_padding_error() -> io::Error {
1046    io::Error::new(
1047        io::ErrorKind::InvalidInput,
1048        "base64 decoder received trailing input after padding",
1049    )
1050}
1051
1052fn stream_decoder_failed_error() -> io::Error {
1053    io::Error::new(
1054        io::ErrorKind::InvalidInput,
1055        "base64 stream decoder is failed after malformed input",
1056    )
1057}
1058
1059fn stream_encoder_failed_error() -> io::Error {
1060    io::Error::new(
1061        io::ErrorKind::InvalidInput,
1062        "base64 stream encoder is failed after internal error",
1063    )
1064}
1065
1066/// A streaming Base64 decoder for `std::io::Read`.
1067///
1068/// For padded engines, this reader stops at the terminal padded Base64
1069/// block and leaves later bytes unread in the wrapped reader. This preserves
1070/// boundaries for callers that decode one Base64 payload from a larger
1071/// stream.
1072///
1073/// # Security
1074///
1075/// This adapter uses the normal strict decoder, not the [`crate::ct`]
1076/// module. It may branch or return early based on malformed input and it
1077/// preserves strict error diagnostics. Do not use it for secret-bearing
1078/// payloads when malformed-input timing matters; decode a complete frame
1079/// with the matching `ct` engine instead.
1080pub struct DecoderReader<R, A, const PAD: bool>
1081where
1082    A: Alphabet,
1083{
1084    inner: Option<R>,
1085    engine: Engine<A, PAD>,
1086    pending: [u8; 4],
1087    pending_len: usize,
1088    output: OutputQueue<3>,
1089    finished: bool,
1090    terminal_seen: bool,
1091    failed: bool,
1092}
1093
1094impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1095where
1096    A: Alphabet,
1097{
1098    /// Creates a new streaming decoder reader.
1099    ///
1100    /// # Security
1101    ///
1102    /// Streaming decoder readers use the normal strict decode path. They
1103    /// are not constant-time-oriented secret decoders.
1104    #[must_use]
1105    pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1106        Self {
1107            inner: Some(inner),
1108            engine,
1109            pending: [0; 4],
1110            pending_len: 0,
1111            output: OutputQueue::new(),
1112            finished: false,
1113            terminal_seen: false,
1114            failed: false,
1115        }
1116    }
1117
1118    /// Returns a shared reference to the wrapped reader.
1119    #[must_use]
1120    pub fn get_ref(&self) -> &R {
1121        self.inner_ref()
1122    }
1123
1124    /// Returns a mutable reference to the wrapped reader.
1125    pub fn get_mut(&mut self) -> &mut R {
1126        self.inner_mut()
1127    }
1128
1129    /// Returns the Base64 engine used by this adapter.
1130    #[must_use]
1131    pub const fn engine(&self) -> Engine<A, PAD> {
1132        self.engine
1133    }
1134
1135    /// Returns whether this adapter uses padded Base64.
1136    #[must_use]
1137    pub const fn is_padded(&self) -> bool {
1138        PAD
1139    }
1140
1141    /// Returns the number of encoded input bytes currently buffered until
1142    /// a complete 4-byte Base64 decode quantum is available.
1143    #[must_use]
1144    pub const fn pending_len(&self) -> usize {
1145        self.pending_len
1146    }
1147
1148    /// Returns whether this decoder reader currently holds a partial input
1149    /// quantum.
1150    #[must_use]
1151    pub const fn has_pending_input(&self) -> bool {
1152        self.pending_len != 0
1153    }
1154
1155    /// Returns how many additional encoded input bytes are needed to
1156    /// complete the currently buffered decode quantum.
1157    ///
1158    /// Returns `0` when no partial input quantum is buffered.
1159    #[must_use]
1160    pub const fn pending_input_needed_len(&self) -> usize {
1161        if self.has_pending_input() {
1162            4 - self.pending_len
1163        } else {
1164            0
1165        }
1166    }
1167
1168    /// Returns the number of decoded bytes currently buffered and ready to
1169    /// be read before this adapter polls the wrapped reader again.
1170    #[must_use]
1171    pub const fn buffered_output_len(&self) -> usize {
1172        self.output.len()
1173    }
1174
1175    /// Returns the maximum number of decoded bytes this adapter can buffer
1176    /// before returning bytes to the caller.
1177    #[must_use]
1178    pub const fn buffered_output_capacity(&self) -> usize {
1179        self.output.capacity()
1180    }
1181
1182    /// Returns how many more decoded bytes can be buffered before this
1183    /// adapter must return bytes to the caller.
1184    #[must_use]
1185    pub const fn buffered_output_remaining_capacity(&self) -> usize {
1186        self.output.available_capacity()
1187    }
1188
1189    /// Returns whether this decoder reader currently has decoded output
1190    /// waiting in its internal queue.
1191    #[must_use]
1192    pub const fn has_buffered_output(&self) -> bool {
1193        !self.output.is_empty()
1194    }
1195
1196    /// Returns whether this decoder reader has seen terminal padding.
1197    ///
1198    /// For padded engines, this becomes `true` after the terminal padded
1199    /// block is decoded. The wrapped reader is then left positioned after
1200    /// that Base64 block so adjacent framed bytes can be read by the
1201    /// caller.
1202    #[must_use]
1203    pub const fn has_terminal_padding(&self) -> bool {
1204        self.terminal_seen
1205    }
1206
1207    /// Returns whether this decoder reader has reached EOF or terminal
1208    /// padding in the wrapped reader.
1209    ///
1210    /// This may become `true` before [`Self::is_finished`] when decoded
1211    /// output is still buffered for the caller.
1212    #[must_use]
1213    pub const fn has_finished_input(&self) -> bool {
1214        self.finished
1215    }
1216
1217    /// Returns whether this reader has reached EOF or terminal padding
1218    /// and has no decoded output buffered for the caller.
1219    #[must_use]
1220    pub const fn is_finished(&self) -> bool {
1221        self.finished && self.output.is_empty()
1222    }
1223
1224    /// Returns whether this decoder reader has rejected malformed Base64
1225    /// input.
1226    ///
1227    /// Once this returns `true`, later reads return an error. The unchecked
1228    /// [`Self::into_inner`] method can still be used for explicit recovery
1229    /// of the wrapped reader.
1230    #[must_use]
1231    pub const fn is_failed(&self) -> bool {
1232        self.failed
1233    }
1234
1235    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
1236    /// reader without discarding buffered decoded output.
1237    #[must_use]
1238    pub const fn can_into_inner(&self) -> bool {
1239        !self.is_failed() && self.is_finished()
1240    }
1241
1242    /// Consumes the decoder reader and returns the wrapped reader.
1243    #[must_use]
1244    pub fn into_inner(mut self) -> R {
1245        self.take_inner()
1246    }
1247
1248    /// Consumes the decoder reader only after the Base64 payload is fully
1249    /// drained.
1250    ///
1251    /// For padded streams, terminal padding may leave adjacent framed bytes
1252    /// unread in the wrapped reader. This method succeeds only after all
1253    /// decoded output buffered by this adapter has been read, so recovering
1254    /// the wrapped reader does not silently discard decoded bytes.
1255    #[allow(clippy::result_large_err)]
1256    pub fn try_into_inner(mut self) -> Result<R, Self> {
1257        if !self.can_into_inner() {
1258            return Err(self);
1259        }
1260        Ok(self.take_inner())
1261    }
1262
1263    fn inner_ref(&self) -> &R {
1264        match &self.inner {
1265            Some(inner) => inner,
1266            None => unreachable!("stream decoder reader inner reader was already taken"),
1267        }
1268    }
1269
1270    fn inner_mut(&mut self) -> &mut R {
1271        match &mut self.inner {
1272            Some(inner) => inner,
1273            None => unreachable!("stream decoder reader inner reader was already taken"),
1274        }
1275    }
1276
1277    fn take_inner(&mut self) -> R {
1278        match self.inner.take() {
1279            Some(inner) => inner,
1280            None => unreachable!("stream decoder reader inner reader was already taken"),
1281        }
1282    }
1283
1284    fn clear_pending(&mut self) {
1285        crate::wipe_bytes(&mut self.pending);
1286        self.pending_len = 0;
1287    }
1288}
1289
1290impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
1291where
1292    A: Alphabet,
1293{
1294    fn drop(&mut self) {
1295        self.clear_pending();
1296        self.output.clear_all();
1297    }
1298}
1299
1300impl<R, A, const PAD: bool> core::fmt::Debug for DecoderReader<R, A, PAD>
1301where
1302    A: Alphabet,
1303{
1304    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1305        formatter
1306            .debug_struct("DecoderReader")
1307            .field("inner", &redacted_inner_state(self.inner.is_some()))
1308            .field("engine", &self.engine)
1309            .field("pending", &"<redacted>")
1310            .field("pending_len", &self.pending_len)
1311            .field("pending_input_needed_len", &self.pending_input_needed_len())
1312            .field("buffered_output_len", &self.output.len())
1313            .field("buffered_output_capacity", &self.output.capacity())
1314            .field(
1315                "buffered_output_remaining_capacity",
1316                &self.output.available_capacity(),
1317            )
1318            .field("can_into_inner", &self.can_into_inner())
1319            .field("finished", &self.finished)
1320            .field("terminal_padding", &self.terminal_seen)
1321            .field("failed", &self.failed)
1322            .finish()
1323    }
1324}
1325
1326impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
1327where
1328    R: Read,
1329    A: Alphabet,
1330{
1331    fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1332        if output.is_empty() {
1333            return Ok(0);
1334        }
1335        if self.failed {
1336            return Err(stream_decoder_failed_error());
1337        }
1338
1339        while self.output.is_empty() && !self.finished {
1340            self.fill_output()?;
1341        }
1342
1343        Ok(self.output.pop_slice(output))
1344    }
1345}
1346
1347impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1348where
1349    R: Read,
1350    A: Alphabet,
1351{
1352    fn fill_output(&mut self) -> io::Result<()> {
1353        if self.failed {
1354            return Err(stream_decoder_failed_error());
1355        }
1356        if self.terminal_seen {
1357            self.finished = true;
1358            return Ok(());
1359        }
1360
1361        let mut input = [0u8; 4];
1362        let available = 4 - self.pending_len;
1363        let read = match self.inner_mut().read(&mut input[..available]) {
1364            Ok(read) => read,
1365            Err(err) => {
1366                crate::wipe_bytes(&mut input);
1367                return Err(err);
1368            }
1369        };
1370        if read == 0 {
1371            crate::wipe_bytes(&mut input);
1372            self.finished = true;
1373            self.push_final_pending()?;
1374            return Ok(());
1375        }
1376
1377        self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
1378        crate::wipe_bytes(&mut input);
1379        self.pending_len += read;
1380        if self.pending_len < 4 {
1381            return Ok(());
1382        }
1383
1384        let mut quad = self.pending;
1385        self.clear_pending();
1386        let result = self.push_decoded(&quad);
1387        crate::wipe_bytes(&mut quad);
1388        result?;
1389        if self.terminal_seen {
1390            self.finished = true;
1391        }
1392        Ok(())
1393    }
1394
1395    fn push_final_pending(&mut self) -> io::Result<()> {
1396        if self.pending_len == 0 {
1397            return Ok(());
1398        }
1399
1400        let mut pending = [0u8; 4];
1401        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1402        let pending_len = self.pending_len;
1403        self.clear_pending();
1404        let result = self.push_decoded(&pending[..pending_len]);
1405        crate::wipe_bytes(&mut pending);
1406        result
1407    }
1408
1409    fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
1410        let mut decoded = [0u8; 3];
1411        let written = match self.engine.decode_slice(input, &mut decoded) {
1412            Ok(written) => written,
1413            Err(err) => {
1414                crate::wipe_bytes(&mut decoded);
1415                self.failed = true;
1416                return Err(decode_error_to_io(err));
1417            }
1418        };
1419        let result = self.output.push_slice(&decoded[..written]);
1420        crate::wipe_bytes(&mut decoded);
1421        result?;
1422        if input.len() == 4 && written < 3 {
1423            self.terminal_seen = true;
1424        }
1425        Ok(())
1426    }
1427}
1428
1429/// A streaming Base64 encoder for `std::io::Read`.
1430pub struct EncoderReader<R, A, const PAD: bool>
1431where
1432    A: Alphabet,
1433{
1434    inner: Option<R>,
1435    engine: Engine<A, PAD>,
1436    pending: [u8; 2],
1437    pending_len: usize,
1438    output: OutputQueue<1024>,
1439    finished: bool,
1440    failed: bool,
1441}
1442
1443impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1444where
1445    A: Alphabet,
1446{
1447    /// Creates a new streaming encoder reader.
1448    #[must_use]
1449    pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1450        Self {
1451            inner: Some(inner),
1452            engine,
1453            pending: [0; 2],
1454            pending_len: 0,
1455            output: OutputQueue::new(),
1456            finished: false,
1457            failed: false,
1458        }
1459    }
1460
1461    /// Returns a shared reference to the wrapped reader.
1462    #[must_use]
1463    pub fn get_ref(&self) -> &R {
1464        self.inner_ref()
1465    }
1466
1467    /// Returns a mutable reference to the wrapped reader.
1468    pub fn get_mut(&mut self) -> &mut R {
1469        self.inner_mut()
1470    }
1471
1472    /// Returns the Base64 engine used by this adapter.
1473    #[must_use]
1474    pub const fn engine(&self) -> Engine<A, PAD> {
1475        self.engine
1476    }
1477
1478    /// Returns whether this adapter uses padded Base64.
1479    #[must_use]
1480    pub const fn is_padded(&self) -> bool {
1481        PAD
1482    }
1483
1484    /// Returns the number of raw input bytes currently buffered until a
1485    /// complete 3-byte Base64 encode quantum is available.
1486    #[must_use]
1487    pub const fn pending_len(&self) -> usize {
1488        self.pending_len
1489    }
1490
1491    /// Returns whether this encoder reader currently holds a partial input
1492    /// quantum.
1493    #[must_use]
1494    pub const fn has_pending_input(&self) -> bool {
1495        self.pending_len != 0
1496    }
1497
1498    /// Returns how many additional raw input bytes are needed to complete
1499    /// the currently buffered encode quantum.
1500    ///
1501    /// Returns `0` when no partial input quantum is buffered.
1502    #[must_use]
1503    pub const fn pending_input_needed_len(&self) -> usize {
1504        if self.has_pending_input() {
1505            3 - self.pending_len
1506        } else {
1507            0
1508        }
1509    }
1510
1511    /// Returns the number of encoded bytes currently buffered and ready to
1512    /// be read before this adapter polls the wrapped reader again.
1513    #[must_use]
1514    pub const fn buffered_output_len(&self) -> usize {
1515        self.output.len()
1516    }
1517
1518    /// Returns the maximum number of encoded bytes this adapter can buffer
1519    /// before returning bytes to the caller.
1520    #[must_use]
1521    pub const fn buffered_output_capacity(&self) -> usize {
1522        self.output.capacity()
1523    }
1524
1525    /// Returns how many more encoded bytes can be buffered before this
1526    /// adapter must return bytes to the caller.
1527    #[must_use]
1528    pub const fn buffered_output_remaining_capacity(&self) -> usize {
1529        self.output.available_capacity()
1530    }
1531
1532    /// Returns whether this encoder reader currently has encoded output
1533    /// waiting in its internal queue.
1534    #[must_use]
1535    pub const fn has_buffered_output(&self) -> bool {
1536        !self.output.is_empty()
1537    }
1538
1539    /// Returns whether this encoder reader has reached EOF in the wrapped
1540    /// reader.
1541    ///
1542    /// This may become `true` before [`Self::is_finished`] when encoded
1543    /// output is still buffered for the caller.
1544    #[must_use]
1545    pub const fn has_finished_input(&self) -> bool {
1546        self.finished
1547    }
1548
1549    /// Returns whether this reader has reached EOF and has no encoded
1550    /// output buffered for the caller.
1551    #[must_use]
1552    pub const fn is_finished(&self) -> bool {
1553        self.finished && self.output.is_empty()
1554    }
1555
1556    /// Returns whether this adapter has failed closed after an internal
1557    /// stream error.
1558    #[must_use]
1559    pub const fn is_failed(&self) -> bool {
1560        self.failed
1561    }
1562
1563    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
1564    /// reader without discarding pending input or buffered encoded output.
1565    #[must_use]
1566    pub const fn can_into_inner(&self) -> bool {
1567        self.is_finished() && !self.failed
1568    }
1569
1570    /// Consumes the encoder reader and returns the wrapped reader.
1571    #[must_use]
1572    pub fn into_inner(mut self) -> R {
1573        self.take_inner()
1574    }
1575
1576    /// Consumes the encoder reader only after the encoded stream is fully
1577    /// drained.
1578    ///
1579    /// This is a checked alternative to [`Self::into_inner`] for callers
1580    /// that want to avoid accidentally discarding pending input or encoded
1581    /// output buffered inside the adapter.
1582    #[allow(clippy::result_large_err)]
1583    pub fn try_into_inner(mut self) -> Result<R, Self> {
1584        if !self.can_into_inner() {
1585            return Err(self);
1586        }
1587        Ok(self.take_inner())
1588    }
1589
1590    fn inner_ref(&self) -> &R {
1591        match &self.inner {
1592            Some(inner) => inner,
1593            None => unreachable!("stream encoder reader inner reader was already taken"),
1594        }
1595    }
1596
1597    fn inner_mut(&mut self) -> &mut R {
1598        match &mut self.inner {
1599            Some(inner) => inner,
1600            None => unreachable!("stream encoder reader inner reader was already taken"),
1601        }
1602    }
1603
1604    fn take_inner(&mut self) -> R {
1605        match self.inner.take() {
1606            Some(inner) => inner,
1607            None => unreachable!("stream encoder reader inner reader was already taken"),
1608        }
1609    }
1610
1611    fn clear_pending(&mut self) {
1612        crate::wipe_bytes(&mut self.pending);
1613        self.pending_len = 0;
1614    }
1615}
1616
1617impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
1618where
1619    A: Alphabet,
1620{
1621    fn drop(&mut self) {
1622        self.clear_pending();
1623        self.output.clear_all();
1624    }
1625}
1626
1627impl<R, A, const PAD: bool> core::fmt::Debug for EncoderReader<R, A, PAD>
1628where
1629    A: Alphabet,
1630{
1631    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1632        formatter
1633            .debug_struct("EncoderReader")
1634            .field("inner", &redacted_inner_state(self.inner.is_some()))
1635            .field("engine", &self.engine)
1636            .field("pending", &"<redacted>")
1637            .field("pending_len", &self.pending_len)
1638            .field("pending_input_needed_len", &self.pending_input_needed_len())
1639            .field("buffered_output_len", &self.output.len())
1640            .field("buffered_output_capacity", &self.output.capacity())
1641            .field(
1642                "buffered_output_remaining_capacity",
1643                &self.output.available_capacity(),
1644            )
1645            .field("can_into_inner", &self.can_into_inner())
1646            .field("finished", &self.finished)
1647            .field("failed", &self.failed)
1648            .finish()
1649    }
1650}
1651
1652impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
1653where
1654    R: Read,
1655    A: Alphabet,
1656{
1657    fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1658        if self.failed {
1659            return Err(stream_encoder_failed_error());
1660        }
1661
1662        if output.is_empty() {
1663            return Ok(0);
1664        }
1665
1666        while self.output.is_empty() && !self.finished {
1667            self.fill_output()?;
1668        }
1669
1670        Ok(self.output.pop_slice(output))
1671    }
1672}
1673
1674impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1675where
1676    R: Read,
1677    A: Alphabet,
1678{
1679    fn fill_output(&mut self) -> io::Result<()> {
1680        let mut input = [0u8; 768];
1681        let read = match self.inner_mut().read(&mut input) {
1682            Ok(read) => read,
1683            Err(err) => {
1684                crate::wipe_bytes(&mut input);
1685                return Err(err);
1686            }
1687        };
1688        if read == 0 {
1689            crate::wipe_bytes(&mut input);
1690            self.finished = true;
1691            if let Err(err) = self.push_final_pending() {
1692                self.failed = true;
1693                return Err(err);
1694            }
1695            return Ok(());
1696        }
1697
1698        let mut consumed = 0;
1699        if self.pending_len > 0 {
1700            let needed = 3 - self.pending_len;
1701            if read < needed {
1702                self.pending[self.pending_len..self.pending_len + read]
1703                    .copy_from_slice(&input[..read]);
1704                self.pending_len += read;
1705                crate::wipe_bytes(&mut input);
1706                return Ok(());
1707            }
1708
1709            let mut chunk = [0u8; 3];
1710            chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1711            chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1712            let result = self.push_encoded(&chunk);
1713            crate::wipe_bytes(&mut chunk);
1714            if let Err(err) = result {
1715                crate::wipe_bytes(&mut input);
1716                self.failed = true;
1717                return Err(err);
1718            }
1719            self.clear_pending();
1720            consumed += needed;
1721        }
1722
1723        let remaining = &input[consumed..read];
1724        let full_len = remaining.len() / 3 * 3;
1725        let tail_len = remaining.len() - full_len;
1726        let mut tail = [0u8; 2];
1727        tail[..tail_len].copy_from_slice(&remaining[full_len..]);
1728        let result = if full_len > 0 {
1729            self.push_encoded(&remaining[..full_len])
1730        } else {
1731            Ok(())
1732        };
1733        crate::wipe_bytes(&mut input);
1734        if let Err(err) = result {
1735            crate::wipe_bytes(&mut tail);
1736            self.failed = true;
1737            return Err(err);
1738        }
1739        self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
1740        crate::wipe_bytes(&mut tail);
1741        self.pending_len = tail_len;
1742        Ok(())
1743    }
1744
1745    fn push_final_pending(&mut self) -> io::Result<()> {
1746        if self.pending_len == 0 {
1747            return Ok(());
1748        }
1749
1750        let mut pending = [0u8; 2];
1751        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1752        let pending_len = self.pending_len;
1753        self.clear_pending();
1754        let result = self.push_encoded(&pending[..pending_len]);
1755        crate::wipe_bytes(&mut pending);
1756        result
1757    }
1758
1759    fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
1760        let mut encoded = [0u8; 1024];
1761        let written = match self.engine.encode_slice(input, &mut encoded) {
1762            Ok(written) => written,
1763            Err(err) => {
1764                crate::wipe_bytes(&mut encoded);
1765                return Err(encode_error_to_io(err));
1766            }
1767        };
1768        let result = self.output.push_slice(&encoded[..written]);
1769        crate::wipe_bytes(&mut encoded);
1770        result
1771    }
1772}
1773
1774const fn redacted_inner_state(present: bool) -> &'static str {
1775    if present { "<present>" } else { "<taken>" }
1776}