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.
587///
588/// Decoded bytes are written to the wrapped writer as valid quads are
589/// accepted. If a later quad in the same logical frame is malformed, already
590/// written bytes cannot be recalled from sockets, pipes, files, or other
591/// external sinks. For atomic frame semantics, decode into an in-memory buffer
592/// first and transfer to the final writer only after [`Self::finish`] succeeds.
593pub struct Decoder<W, A, const PAD: bool>
594where
595    A: Alphabet,
596{
597    inner: Option<W>,
598    engine: Engine<A, PAD>,
599    pending: [u8; 4],
600    pending_len: usize,
601    output: OutputQueue<1024>,
602    finished: bool,
603    failed: bool,
604    finalized: bool,
605}
606
607impl<W, A, const PAD: bool> Decoder<W, A, PAD>
608where
609    A: Alphabet,
610{
611    /// Creates a new streaming decoder.
612    ///
613    /// # Security
614    ///
615    /// Streaming decoders use the normal strict decode path. They are not
616    /// constant-time-oriented secret decoders.
617    #[must_use]
618    pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
619        Self {
620            inner: Some(inner),
621            engine,
622            pending: [0; 4],
623            pending_len: 0,
624            output: OutputQueue::new(),
625            finished: false,
626            finalized: false,
627            failed: false,
628        }
629    }
630
631    /// Returns a shared reference to the wrapped writer.
632    #[must_use]
633    pub fn get_ref(&self) -> &W {
634        self.inner_ref()
635    }
636
637    /// Returns a mutable reference to the wrapped writer.
638    pub fn get_mut(&mut self) -> &mut W {
639        self.inner_mut()
640    }
641
642    /// Returns the Base64 engine used by this adapter.
643    #[must_use]
644    pub const fn engine(&self) -> Engine<A, PAD> {
645        self.engine
646    }
647
648    /// Returns whether this adapter uses padded Base64.
649    #[must_use]
650    pub const fn is_padded(&self) -> bool {
651        PAD
652    }
653
654    /// Returns the number of encoded input bytes currently buffered until
655    /// a complete 4-byte Base64 decode quantum is available.
656    #[must_use]
657    pub const fn pending_len(&self) -> usize {
658        self.pending_len
659    }
660
661    /// Returns whether this decoder currently holds a partial input
662    /// quantum.
663    #[must_use]
664    pub const fn has_pending_input(&self) -> bool {
665        self.pending_len != 0
666    }
667
668    /// Returns how many additional input bytes are needed to complete the
669    /// currently buffered decode quantum.
670    ///
671    /// Returns `0` when no partial input quantum is buffered.
672    #[must_use]
673    pub const fn pending_input_needed_len(&self) -> usize {
674        if self.has_pending_input() {
675            4 - self.pending_len
676        } else {
677            0
678        }
679    }
680
681    /// Returns the number of decoded bytes buffered for the wrapped writer
682    /// after a previous write or flush could not fully drain them.
683    #[must_use]
684    pub const fn buffered_output_len(&self) -> usize {
685        self.output.len()
686    }
687
688    /// Returns the maximum number of decoded bytes this adapter can buffer
689    /// before returning bytes to the caller.
690    #[must_use]
691    pub const fn buffered_output_capacity(&self) -> usize {
692        self.output.capacity()
693    }
694
695    /// Returns how many more decoded bytes can be buffered before this
696    /// adapter must drain the wrapped writer.
697    #[must_use]
698    pub const fn buffered_output_remaining_capacity(&self) -> usize {
699        self.output.available_capacity()
700    }
701
702    /// Returns whether this decoder has decoded output waiting to be
703    /// written to the wrapped writer.
704    #[must_use]
705    pub const fn has_buffered_output(&self) -> bool {
706        !self.output.is_empty()
707    }
708
709    /// Returns whether this decoder has processed a terminal padded block.
710    ///
711    /// Once this returns `true`, later calls to [`Write::write`] with
712    /// additional input return an error because strict Base64 does not
713    /// permit trailing payload bytes after padding.
714    #[must_use]
715    pub const fn has_terminal_padding(&self) -> bool {
716        self.finished
717    }
718
719    /// Returns whether this decoder has been finalized.
720    ///
721    /// Once this returns `true`, later non-empty writes return an error.
722    #[must_use]
723    pub const fn is_finalized(&self) -> bool {
724        self.finalized
725    }
726
727    /// Returns whether this decoder has rejected malformed Base64 input.
728    ///
729    /// Once this returns `true`, later writes, flushes, and finalization
730    /// attempts return an error. The unchecked [`Self::into_inner`] method
731    /// can still be used for explicit recovery of the wrapped writer.
732    #[must_use]
733    pub const fn is_failed(&self) -> bool {
734        self.failed
735    }
736
737    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
738    /// writer without discarding pending encoded input.
739    #[must_use]
740    pub const fn can_into_inner(&self) -> bool {
741        !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
742    }
743
744    /// Consumes the decoder without flushing pending input.
745    ///
746    /// Prefer [`Self::finish`] when the decoded output must be complete.
747    #[must_use]
748    pub fn into_inner(mut self) -> W {
749        self.take_inner()
750    }
751
752    /// Consumes the decoder only when no partial input quantum is buffered.
753    ///
754    /// This does not flush or finalize the wrapped writer. It is a checked
755    /// alternative to [`Self::into_inner`] for callers that want to avoid
756    /// accidentally discarding pending encoded input bytes.
757    #[allow(clippy::result_large_err)]
758    pub fn try_into_inner(mut self) -> Result<W, Self> {
759        if !self.can_into_inner() {
760            return Err(self);
761        }
762        Ok(self.take_inner())
763    }
764
765    fn inner_ref(&self) -> &W {
766        match &self.inner {
767            Some(inner) => inner,
768            None => unreachable!("stream decoder inner writer was already taken"),
769        }
770    }
771
772    fn inner_mut(&mut self) -> &mut W {
773        match &mut self.inner {
774            Some(inner) => inner,
775            None => unreachable!("stream decoder inner writer was already taken"),
776        }
777    }
778
779    fn take_inner(&mut self) -> W {
780        match self.inner.take() {
781            Some(inner) => inner,
782            None => unreachable!("stream decoder inner writer was already taken"),
783        }
784    }
785
786    fn clear_pending(&mut self) {
787        crate::wipe_bytes(&mut self.pending);
788        self.pending_len = 0;
789    }
790
791    fn clear_output(&mut self) {
792        self.output.clear_all();
793    }
794}
795
796impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
797where
798    A: Alphabet,
799{
800    fn drop(&mut self) {
801        self.clear_pending();
802        self.clear_output();
803    }
804}
805
806impl<W, A, const PAD: bool> core::fmt::Debug for Decoder<W, A, PAD>
807where
808    A: Alphabet,
809{
810    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
811        formatter
812            .debug_struct("Decoder")
813            .field("inner", &redacted_inner_state(self.inner.is_some()))
814            .field("engine", &self.engine)
815            .field("pending", &"<redacted>")
816            .field("pending_len", &self.pending_len)
817            .field("pending_input_needed_len", &self.pending_input_needed_len())
818            .field("buffered_output_len", &self.output.len())
819            .field("buffered_output_capacity", &self.output.capacity())
820            .field(
821                "buffered_output_remaining_capacity",
822                &self.output.available_capacity(),
823            )
824            .field("can_into_inner", &self.can_into_inner())
825            .field("terminal_padding", &self.finished)
826            .field("finalized", &self.finalized)
827            .field("failed", &self.failed)
828            .finish()
829    }
830}
831
832impl<W, A, const PAD: bool> Decoder<W, A, PAD>
833where
834    W: Write,
835    A: Alphabet,
836{
837    /// Validates any final pending input and flushes the wrapped writer
838    /// without consuming this decoder.
839    ///
840    /// After this succeeds, [`Self::pending_len`] returns `0`, later
841    /// writes are rejected, and [`Self::finish`] can still be used to
842    /// recover the wrapped writer.
843    /// If the final buffered input is malformed, an error is returned and
844    /// the caller still owns the decoder for diagnostics or explicit
845    /// recovery.
846    pub fn try_finish(&mut self) -> io::Result<()> {
847        if self.failed {
848            return Err(stream_decoder_failed_error());
849        }
850        if !self.finalized {
851            self.queue_pending_final()?;
852            self.finalized = true;
853        }
854        self.flush()
855    }
856
857    /// Validates final pending input, flushes the wrapped writer, and returns it.
858    pub fn finish(mut self) -> io::Result<W> {
859        self.try_finish()?;
860        Ok(self.take_inner())
861    }
862
863    fn queue_pending_final(&mut self) -> io::Result<()> {
864        if self.pending_len == 0 {
865            return Ok(());
866        }
867
868        let mut pending = [0u8; 4];
869        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
870        let pending_len = self.pending_len;
871        let mut decoded = [0u8; 3];
872        let result = self.queue_decoded_temp(&pending[..pending_len], &mut decoded);
873        crate::wipe_bytes(&mut pending);
874        if let Err(err) = result {
875            self.clear_pending();
876            return Err(err);
877        }
878        self.clear_pending();
879        Ok(())
880    }
881
882    fn queue_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
883        let mut decoded = [0u8; 3];
884        let result = self.queue_decoded_temp(&input, &mut decoded);
885        crate::wipe_bytes(&mut input);
886        let written = result?;
887        if written < 3 {
888            self.finished = true;
889        }
890        Ok(())
891    }
892
893    fn queue_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
894        let written = match self.engine.decode_slice(input, decoded) {
895            Ok(written) => written,
896            Err(err) => {
897                crate::wipe_bytes(decoded);
898                self.failed = true;
899                return Err(decode_error_to_io(err));
900            }
901        };
902
903        let result = self.output.push_slice(&decoded[..written]);
904        crate::wipe_bytes(decoded);
905        if result.is_err() {
906            self.failed = true;
907        }
908        result?;
909        Ok(written)
910    }
911
912    fn drain_output(&mut self) -> io::Result<()> {
913        let mut chunk = [0u8; 1024];
914        while !self.output.is_empty() {
915            let pending = self.output.copy_front(&mut chunk);
916            let result = self.inner_mut().write(&chunk[..pending]);
917            crate::wipe_bytes(&mut chunk[..pending]);
918            match result {
919                Ok(0) => {
920                    return Err(io::Error::new(
921                        io::ErrorKind::WriteZero,
922                        "base64 stream decoder could not drain buffered output",
923                    ));
924                }
925                Ok(written) => {
926                    if written > pending {
927                        self.failed = true;
928                        return Err(io::Error::new(
929                            io::ErrorKind::InvalidData,
930                            "wrapped writer reported more bytes than provided",
931                        ));
932                    }
933                    self.output.discard_front(written);
934                }
935                Err(err) => return Err(err),
936            }
937        }
938
939        Ok(())
940    }
941}
942
943impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
944where
945    W: Write,
946    A: Alphabet,
947{
948    fn write(&mut self, input: &[u8]) -> io::Result<usize> {
949        if self.failed {
950            return Err(stream_decoder_failed_error());
951        }
952        if input.is_empty() {
953            self.drain_output()?;
954            return Ok(0);
955        }
956        self.drain_output()?;
957        if self.finalized {
958            return Err(io::Error::new(
959                io::ErrorKind::InvalidInput,
960                "base64 stream decoder received input after finalization",
961            ));
962        }
963        if self.finished {
964            self.failed = true;
965            return Err(trailing_input_after_padding_error());
966        }
967
968        let mut consumed = 0;
969        if self.pending_len > 0 {
970            let needed = 4 - self.pending_len;
971            if input.len() < needed {
972                self.pending[self.pending_len..self.pending_len + input.len()]
973                    .copy_from_slice(input);
974                self.pending_len += input.len();
975                return Ok(input.len());
976            }
977
978            let mut quad = [0u8; 4];
979            quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
980            quad[self.pending_len..].copy_from_slice(&input[..needed]);
981            let result = self.queue_full_quad(quad);
982            crate::wipe_bytes(&mut quad);
983            if let Err(err) = result {
984                self.clear_pending();
985                return Err(err);
986            }
987            self.clear_pending();
988            consumed += needed;
989
990            if self.finished {
991                return Ok(consumed);
992            }
993        }
994
995        while input.len() - consumed >= 4 {
996            if self.output.available_capacity() < 3 {
997                return Ok(consumed);
998            }
999
1000            let mut quad = [
1001                input[consumed],
1002                input[consumed + 1],
1003                input[consumed + 2],
1004                input[consumed + 3],
1005            ];
1006            let mut decoded = [0u8; 3];
1007            let written = match self.engine.decode_slice(&quad, &mut decoded) {
1008                Ok(written) => written,
1009                Err(err) => {
1010                    crate::wipe_bytes(&mut quad);
1011                    crate::wipe_bytes(&mut decoded);
1012                    self.failed = true;
1013                    if consumed > 0 {
1014                        return Ok(consumed);
1015                    }
1016
1017                    return Err(decode_error_to_io(err));
1018                }
1019            };
1020
1021            let result = self.output.push_slice(&decoded[..written]);
1022            crate::wipe_bytes(&mut quad);
1023            crate::wipe_bytes(&mut decoded);
1024            result?;
1025            consumed += 4;
1026
1027            if written < 3 {
1028                self.finished = true;
1029                return Ok(consumed);
1030            }
1031        }
1032
1033        let tail = &input[consumed..];
1034        self.pending[..tail.len()].copy_from_slice(tail);
1035        self.pending_len = tail.len();
1036        consumed += tail.len();
1037
1038        Ok(consumed)
1039    }
1040
1041    fn flush(&mut self) -> io::Result<()> {
1042        if self.failed {
1043            return Err(stream_decoder_failed_error());
1044        }
1045        self.drain_output()?;
1046        self.inner_mut().flush()
1047    }
1048}
1049
1050fn decode_error_to_io(err: DecodeError) -> io::Error {
1051    io::Error::new(io::ErrorKind::InvalidInput, err)
1052}
1053
1054fn trailing_input_after_padding_error() -> io::Error {
1055    io::Error::new(
1056        io::ErrorKind::InvalidInput,
1057        "base64 decoder received trailing input after padding",
1058    )
1059}
1060
1061fn stream_decoder_failed_error() -> io::Error {
1062    io::Error::new(
1063        io::ErrorKind::InvalidInput,
1064        "base64 stream decoder is failed after malformed input",
1065    )
1066}
1067
1068fn stream_encoder_failed_error() -> io::Error {
1069    io::Error::new(
1070        io::ErrorKind::InvalidInput,
1071        "base64 stream encoder is failed after internal error",
1072    )
1073}
1074
1075/// A streaming Base64 decoder for `std::io::Read`.
1076///
1077/// For padded engines, this reader stops at the terminal padded Base64
1078/// block and leaves later bytes unread in the wrapped reader. This preserves
1079/// boundaries for callers that decode one Base64 payload from a larger
1080/// stream.
1081///
1082/// # Security
1083///
1084/// This adapter uses the normal strict decoder, not the [`crate::ct`]
1085/// module. It may branch or return early based on malformed input and it
1086/// preserves strict error diagnostics. Do not use it for secret-bearing
1087/// payloads when malformed-input timing matters; decode a complete frame
1088/// with the matching `ct` engine instead.
1089pub struct DecoderReader<R, A, const PAD: bool>
1090where
1091    A: Alphabet,
1092{
1093    inner: Option<R>,
1094    engine: Engine<A, PAD>,
1095    pending: [u8; 4],
1096    pending_len: usize,
1097    output: OutputQueue<3>,
1098    finished: bool,
1099    terminal_seen: bool,
1100    failed: bool,
1101}
1102
1103impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1104where
1105    A: Alphabet,
1106{
1107    /// Creates a new streaming decoder reader.
1108    ///
1109    /// # Security
1110    ///
1111    /// Streaming decoder readers use the normal strict decode path. They
1112    /// are not constant-time-oriented secret decoders.
1113    #[must_use]
1114    pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1115        Self {
1116            inner: Some(inner),
1117            engine,
1118            pending: [0; 4],
1119            pending_len: 0,
1120            output: OutputQueue::new(),
1121            finished: false,
1122            terminal_seen: false,
1123            failed: false,
1124        }
1125    }
1126
1127    /// Returns a shared reference to the wrapped reader.
1128    #[must_use]
1129    pub fn get_ref(&self) -> &R {
1130        self.inner_ref()
1131    }
1132
1133    /// Returns a mutable reference to the wrapped reader.
1134    pub fn get_mut(&mut self) -> &mut R {
1135        self.inner_mut()
1136    }
1137
1138    /// Returns the Base64 engine used by this adapter.
1139    #[must_use]
1140    pub const fn engine(&self) -> Engine<A, PAD> {
1141        self.engine
1142    }
1143
1144    /// Returns whether this adapter uses padded Base64.
1145    #[must_use]
1146    pub const fn is_padded(&self) -> bool {
1147        PAD
1148    }
1149
1150    /// Returns the number of encoded input bytes currently buffered until
1151    /// a complete 4-byte Base64 decode quantum is available.
1152    #[must_use]
1153    pub const fn pending_len(&self) -> usize {
1154        self.pending_len
1155    }
1156
1157    /// Returns whether this decoder reader currently holds a partial input
1158    /// quantum.
1159    #[must_use]
1160    pub const fn has_pending_input(&self) -> bool {
1161        self.pending_len != 0
1162    }
1163
1164    /// Returns how many additional encoded input bytes are needed to
1165    /// complete the currently buffered decode quantum.
1166    ///
1167    /// Returns `0` when no partial input quantum is buffered.
1168    #[must_use]
1169    pub const fn pending_input_needed_len(&self) -> usize {
1170        if self.has_pending_input() {
1171            4 - self.pending_len
1172        } else {
1173            0
1174        }
1175    }
1176
1177    /// Returns the number of decoded bytes currently buffered and ready to
1178    /// be read before this adapter polls the wrapped reader again.
1179    #[must_use]
1180    pub const fn buffered_output_len(&self) -> usize {
1181        self.output.len()
1182    }
1183
1184    /// Returns the maximum number of decoded bytes this adapter can buffer
1185    /// before returning bytes to the caller.
1186    #[must_use]
1187    pub const fn buffered_output_capacity(&self) -> usize {
1188        self.output.capacity()
1189    }
1190
1191    /// Returns how many more decoded bytes can be buffered before this
1192    /// adapter must return bytes to the caller.
1193    #[must_use]
1194    pub const fn buffered_output_remaining_capacity(&self) -> usize {
1195        self.output.available_capacity()
1196    }
1197
1198    /// Returns whether this decoder reader currently has decoded output
1199    /// waiting in its internal queue.
1200    #[must_use]
1201    pub const fn has_buffered_output(&self) -> bool {
1202        !self.output.is_empty()
1203    }
1204
1205    /// Returns whether this decoder reader has seen terminal padding.
1206    ///
1207    /// For padded engines, this becomes `true` after the terminal padded
1208    /// block is decoded. The wrapped reader is then left positioned after
1209    /// that Base64 block so adjacent framed bytes can be read by the
1210    /// caller.
1211    #[must_use]
1212    pub const fn has_terminal_padding(&self) -> bool {
1213        self.terminal_seen
1214    }
1215
1216    /// Returns whether this decoder reader has reached EOF or terminal
1217    /// padding in the wrapped reader.
1218    ///
1219    /// This may become `true` before [`Self::is_finished`] when decoded
1220    /// output is still buffered for the caller.
1221    #[must_use]
1222    pub const fn has_finished_input(&self) -> bool {
1223        self.finished
1224    }
1225
1226    /// Returns whether this reader has reached EOF or terminal padding
1227    /// and has no decoded output buffered for the caller.
1228    #[must_use]
1229    pub const fn is_finished(&self) -> bool {
1230        self.finished && self.output.is_empty()
1231    }
1232
1233    /// Returns whether this decoder reader has rejected malformed Base64
1234    /// input.
1235    ///
1236    /// Once this returns `true`, later reads return an error. The unchecked
1237    /// [`Self::into_inner`] method can still be used for explicit recovery
1238    /// of the wrapped reader.
1239    #[must_use]
1240    pub const fn is_failed(&self) -> bool {
1241        self.failed
1242    }
1243
1244    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
1245    /// reader without discarding buffered decoded output.
1246    #[must_use]
1247    pub const fn can_into_inner(&self) -> bool {
1248        !self.is_failed() && self.is_finished()
1249    }
1250
1251    /// Consumes the decoder reader and returns the wrapped reader.
1252    #[must_use]
1253    pub fn into_inner(mut self) -> R {
1254        self.take_inner()
1255    }
1256
1257    /// Consumes the decoder reader only after the Base64 payload is fully
1258    /// drained.
1259    ///
1260    /// For padded streams, terminal padding may leave adjacent framed bytes
1261    /// unread in the wrapped reader. This method succeeds only after all
1262    /// decoded output buffered by this adapter has been read, so recovering
1263    /// the wrapped reader does not silently discard decoded bytes.
1264    #[allow(clippy::result_large_err)]
1265    pub fn try_into_inner(mut self) -> Result<R, Self> {
1266        if !self.can_into_inner() {
1267            return Err(self);
1268        }
1269        Ok(self.take_inner())
1270    }
1271
1272    fn inner_ref(&self) -> &R {
1273        match &self.inner {
1274            Some(inner) => inner,
1275            None => unreachable!("stream decoder reader inner reader was already taken"),
1276        }
1277    }
1278
1279    fn inner_mut(&mut self) -> &mut R {
1280        match &mut self.inner {
1281            Some(inner) => inner,
1282            None => unreachable!("stream decoder reader inner reader was already taken"),
1283        }
1284    }
1285
1286    fn take_inner(&mut self) -> R {
1287        match self.inner.take() {
1288            Some(inner) => inner,
1289            None => unreachable!("stream decoder reader inner reader was already taken"),
1290        }
1291    }
1292
1293    fn clear_pending(&mut self) {
1294        crate::wipe_bytes(&mut self.pending);
1295        self.pending_len = 0;
1296    }
1297}
1298
1299impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
1300where
1301    A: Alphabet,
1302{
1303    fn drop(&mut self) {
1304        self.clear_pending();
1305        self.output.clear_all();
1306    }
1307}
1308
1309impl<R, A, const PAD: bool> core::fmt::Debug for DecoderReader<R, A, PAD>
1310where
1311    A: Alphabet,
1312{
1313    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1314        formatter
1315            .debug_struct("DecoderReader")
1316            .field("inner", &redacted_inner_state(self.inner.is_some()))
1317            .field("engine", &self.engine)
1318            .field("pending", &"<redacted>")
1319            .field("pending_len", &self.pending_len)
1320            .field("pending_input_needed_len", &self.pending_input_needed_len())
1321            .field("buffered_output_len", &self.output.len())
1322            .field("buffered_output_capacity", &self.output.capacity())
1323            .field(
1324                "buffered_output_remaining_capacity",
1325                &self.output.available_capacity(),
1326            )
1327            .field("can_into_inner", &self.can_into_inner())
1328            .field("finished", &self.finished)
1329            .field("terminal_padding", &self.terminal_seen)
1330            .field("failed", &self.failed)
1331            .finish()
1332    }
1333}
1334
1335impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
1336where
1337    R: Read,
1338    A: Alphabet,
1339{
1340    fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1341        if output.is_empty() {
1342            return Ok(0);
1343        }
1344        if self.failed {
1345            return Err(stream_decoder_failed_error());
1346        }
1347
1348        while self.output.is_empty() && !self.finished {
1349            self.fill_output()?;
1350        }
1351
1352        Ok(self.output.pop_slice(output))
1353    }
1354}
1355
1356impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1357where
1358    R: Read,
1359    A: Alphabet,
1360{
1361    fn fill_output(&mut self) -> io::Result<()> {
1362        if self.failed {
1363            return Err(stream_decoder_failed_error());
1364        }
1365        if self.terminal_seen {
1366            self.finished = true;
1367            return Ok(());
1368        }
1369
1370        let mut input = [0u8; 4];
1371        let available = 4 - self.pending_len;
1372        let read = match self.inner_mut().read(&mut input[..available]) {
1373            Ok(read) => read,
1374            Err(err) => {
1375                crate::wipe_bytes(&mut input);
1376                return Err(err);
1377            }
1378        };
1379        if read == 0 {
1380            crate::wipe_bytes(&mut input);
1381            self.finished = true;
1382            self.push_final_pending()?;
1383            return Ok(());
1384        }
1385
1386        self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
1387        crate::wipe_bytes(&mut input);
1388        self.pending_len += read;
1389        if self.pending_len < 4 {
1390            return Ok(());
1391        }
1392
1393        let mut quad = self.pending;
1394        self.clear_pending();
1395        let result = self.push_decoded(&quad);
1396        crate::wipe_bytes(&mut quad);
1397        result?;
1398        if self.terminal_seen {
1399            self.finished = true;
1400        }
1401        Ok(())
1402    }
1403
1404    fn push_final_pending(&mut self) -> io::Result<()> {
1405        if self.pending_len == 0 {
1406            return Ok(());
1407        }
1408
1409        let mut pending = [0u8; 4];
1410        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1411        let pending_len = self.pending_len;
1412        self.clear_pending();
1413        let result = self.push_decoded(&pending[..pending_len]);
1414        crate::wipe_bytes(&mut pending);
1415        result
1416    }
1417
1418    fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
1419        let mut decoded = [0u8; 3];
1420        let written = match self.engine.decode_slice(input, &mut decoded) {
1421            Ok(written) => written,
1422            Err(err) => {
1423                crate::wipe_bytes(&mut decoded);
1424                self.failed = true;
1425                return Err(decode_error_to_io(err));
1426            }
1427        };
1428        let result = self.output.push_slice(&decoded[..written]);
1429        crate::wipe_bytes(&mut decoded);
1430        if result.is_err() {
1431            self.failed = true;
1432        }
1433        result?;
1434        if input.len() == 4 && written < 3 {
1435            self.terminal_seen = true;
1436        }
1437        Ok(())
1438    }
1439}
1440
1441/// A streaming Base64 encoder for `std::io::Read`.
1442pub struct EncoderReader<R, A, const PAD: bool>
1443where
1444    A: Alphabet,
1445{
1446    inner: Option<R>,
1447    engine: Engine<A, PAD>,
1448    pending: [u8; 2],
1449    pending_len: usize,
1450    output: OutputQueue<1024>,
1451    finished: bool,
1452    failed: bool,
1453}
1454
1455impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1456where
1457    A: Alphabet,
1458{
1459    /// Creates a new streaming encoder reader.
1460    #[must_use]
1461    pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1462        Self {
1463            inner: Some(inner),
1464            engine,
1465            pending: [0; 2],
1466            pending_len: 0,
1467            output: OutputQueue::new(),
1468            finished: false,
1469            failed: false,
1470        }
1471    }
1472
1473    /// Returns a shared reference to the wrapped reader.
1474    #[must_use]
1475    pub fn get_ref(&self) -> &R {
1476        self.inner_ref()
1477    }
1478
1479    /// Returns a mutable reference to the wrapped reader.
1480    pub fn get_mut(&mut self) -> &mut R {
1481        self.inner_mut()
1482    }
1483
1484    /// Returns the Base64 engine used by this adapter.
1485    #[must_use]
1486    pub const fn engine(&self) -> Engine<A, PAD> {
1487        self.engine
1488    }
1489
1490    /// Returns whether this adapter uses padded Base64.
1491    #[must_use]
1492    pub const fn is_padded(&self) -> bool {
1493        PAD
1494    }
1495
1496    /// Returns the number of raw input bytes currently buffered until a
1497    /// complete 3-byte Base64 encode quantum is available.
1498    #[must_use]
1499    pub const fn pending_len(&self) -> usize {
1500        self.pending_len
1501    }
1502
1503    /// Returns whether this encoder reader currently holds a partial input
1504    /// quantum.
1505    #[must_use]
1506    pub const fn has_pending_input(&self) -> bool {
1507        self.pending_len != 0
1508    }
1509
1510    /// Returns how many additional raw input bytes are needed to complete
1511    /// the currently buffered encode quantum.
1512    ///
1513    /// Returns `0` when no partial input quantum is buffered.
1514    #[must_use]
1515    pub const fn pending_input_needed_len(&self) -> usize {
1516        if self.has_pending_input() {
1517            3 - self.pending_len
1518        } else {
1519            0
1520        }
1521    }
1522
1523    /// Returns the number of encoded bytes currently buffered and ready to
1524    /// be read before this adapter polls the wrapped reader again.
1525    #[must_use]
1526    pub const fn buffered_output_len(&self) -> usize {
1527        self.output.len()
1528    }
1529
1530    /// Returns the maximum number of encoded bytes this adapter can buffer
1531    /// before returning bytes to the caller.
1532    #[must_use]
1533    pub const fn buffered_output_capacity(&self) -> usize {
1534        self.output.capacity()
1535    }
1536
1537    /// Returns how many more encoded bytes can be buffered before this
1538    /// adapter must return bytes to the caller.
1539    #[must_use]
1540    pub const fn buffered_output_remaining_capacity(&self) -> usize {
1541        self.output.available_capacity()
1542    }
1543
1544    /// Returns whether this encoder reader currently has encoded output
1545    /// waiting in its internal queue.
1546    #[must_use]
1547    pub const fn has_buffered_output(&self) -> bool {
1548        !self.output.is_empty()
1549    }
1550
1551    /// Returns whether this encoder reader has reached EOF in the wrapped
1552    /// reader.
1553    ///
1554    /// This may become `true` before [`Self::is_finished`] when encoded
1555    /// output is still buffered for the caller.
1556    #[must_use]
1557    pub const fn has_finished_input(&self) -> bool {
1558        self.finished
1559    }
1560
1561    /// Returns whether this reader has reached EOF and has no encoded
1562    /// output buffered for the caller.
1563    #[must_use]
1564    pub const fn is_finished(&self) -> bool {
1565        self.finished && self.output.is_empty()
1566    }
1567
1568    /// Returns whether this adapter has failed closed after an internal
1569    /// stream error.
1570    #[must_use]
1571    pub const fn is_failed(&self) -> bool {
1572        self.failed
1573    }
1574
1575    /// Returns whether [`Self::try_into_inner`] can recover the wrapped
1576    /// reader without discarding pending input or buffered encoded output.
1577    #[must_use]
1578    pub const fn can_into_inner(&self) -> bool {
1579        self.is_finished() && !self.failed
1580    }
1581
1582    /// Consumes the encoder reader and returns the wrapped reader.
1583    #[must_use]
1584    pub fn into_inner(mut self) -> R {
1585        self.take_inner()
1586    }
1587
1588    /// Consumes the encoder reader only after the encoded stream is fully
1589    /// drained.
1590    ///
1591    /// This is a checked alternative to [`Self::into_inner`] for callers
1592    /// that want to avoid accidentally discarding pending input or encoded
1593    /// output buffered inside the adapter.
1594    #[allow(clippy::result_large_err)]
1595    pub fn try_into_inner(mut self) -> Result<R, Self> {
1596        if !self.can_into_inner() {
1597            return Err(self);
1598        }
1599        Ok(self.take_inner())
1600    }
1601
1602    fn inner_ref(&self) -> &R {
1603        match &self.inner {
1604            Some(inner) => inner,
1605            None => unreachable!("stream encoder reader inner reader was already taken"),
1606        }
1607    }
1608
1609    fn inner_mut(&mut self) -> &mut R {
1610        match &mut self.inner {
1611            Some(inner) => inner,
1612            None => unreachable!("stream encoder reader inner reader was already taken"),
1613        }
1614    }
1615
1616    fn take_inner(&mut self) -> R {
1617        match self.inner.take() {
1618            Some(inner) => inner,
1619            None => unreachable!("stream encoder reader inner reader was already taken"),
1620        }
1621    }
1622
1623    fn clear_pending(&mut self) {
1624        crate::wipe_bytes(&mut self.pending);
1625        self.pending_len = 0;
1626    }
1627}
1628
1629impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
1630where
1631    A: Alphabet,
1632{
1633    fn drop(&mut self) {
1634        self.clear_pending();
1635        self.output.clear_all();
1636    }
1637}
1638
1639impl<R, A, const PAD: bool> core::fmt::Debug for EncoderReader<R, A, PAD>
1640where
1641    A: Alphabet,
1642{
1643    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1644        formatter
1645            .debug_struct("EncoderReader")
1646            .field("inner", &redacted_inner_state(self.inner.is_some()))
1647            .field("engine", &self.engine)
1648            .field("pending", &"<redacted>")
1649            .field("pending_len", &self.pending_len)
1650            .field("pending_input_needed_len", &self.pending_input_needed_len())
1651            .field("buffered_output_len", &self.output.len())
1652            .field("buffered_output_capacity", &self.output.capacity())
1653            .field(
1654                "buffered_output_remaining_capacity",
1655                &self.output.available_capacity(),
1656            )
1657            .field("can_into_inner", &self.can_into_inner())
1658            .field("finished", &self.finished)
1659            .field("failed", &self.failed)
1660            .finish()
1661    }
1662}
1663
1664impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
1665where
1666    R: Read,
1667    A: Alphabet,
1668{
1669    fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1670        if self.failed {
1671            return Err(stream_encoder_failed_error());
1672        }
1673
1674        if output.is_empty() {
1675            return Ok(0);
1676        }
1677
1678        while self.output.is_empty() && !self.finished {
1679            self.fill_output()?;
1680        }
1681
1682        Ok(self.output.pop_slice(output))
1683    }
1684}
1685
1686impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1687where
1688    R: Read,
1689    A: Alphabet,
1690{
1691    fn fill_output(&mut self) -> io::Result<()> {
1692        let mut input = [0u8; 768];
1693        let read = match self.inner_mut().read(&mut input) {
1694            Ok(read) => read,
1695            Err(err) => {
1696                crate::wipe_bytes(&mut input);
1697                return Err(err);
1698            }
1699        };
1700        if read == 0 {
1701            crate::wipe_bytes(&mut input);
1702            self.finished = true;
1703            if let Err(err) = self.push_final_pending() {
1704                self.failed = true;
1705                return Err(err);
1706            }
1707            return Ok(());
1708        }
1709
1710        let mut consumed = 0;
1711        if self.pending_len > 0 {
1712            let needed = 3 - self.pending_len;
1713            if read < needed {
1714                self.pending[self.pending_len..self.pending_len + read]
1715                    .copy_from_slice(&input[..read]);
1716                self.pending_len += read;
1717                crate::wipe_bytes(&mut input);
1718                return Ok(());
1719            }
1720
1721            let mut chunk = [0u8; 3];
1722            chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1723            chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1724            let result = self.push_encoded(&chunk);
1725            crate::wipe_bytes(&mut chunk);
1726            if let Err(err) = result {
1727                crate::wipe_bytes(&mut input);
1728                self.failed = true;
1729                return Err(err);
1730            }
1731            self.clear_pending();
1732            consumed += needed;
1733        }
1734
1735        let remaining = &input[consumed..read];
1736        let full_len = remaining.len() / 3 * 3;
1737        let tail_len = remaining.len() - full_len;
1738        let mut tail = [0u8; 2];
1739        tail[..tail_len].copy_from_slice(&remaining[full_len..]);
1740        let result = if full_len > 0 {
1741            self.push_encoded(&remaining[..full_len])
1742        } else {
1743            Ok(())
1744        };
1745        crate::wipe_bytes(&mut input);
1746        if let Err(err) = result {
1747            crate::wipe_bytes(&mut tail);
1748            self.failed = true;
1749            return Err(err);
1750        }
1751        self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
1752        crate::wipe_bytes(&mut tail);
1753        self.pending_len = tail_len;
1754        Ok(())
1755    }
1756
1757    fn push_final_pending(&mut self) -> io::Result<()> {
1758        if self.pending_len == 0 {
1759            return Ok(());
1760        }
1761
1762        let mut pending = [0u8; 2];
1763        pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1764        let pending_len = self.pending_len;
1765        self.clear_pending();
1766        let result = self.push_encoded(&pending[..pending_len]);
1767        crate::wipe_bytes(&mut pending);
1768        result
1769    }
1770
1771    fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
1772        let mut encoded = [0u8; 1024];
1773        let written = match self.engine.encode_slice(input, &mut encoded) {
1774            Ok(written) => written,
1775            Err(err) => {
1776                crate::wipe_bytes(&mut encoded);
1777                return Err(encode_error_to_io(err));
1778            }
1779        };
1780        let result = self.output.push_slice(&encoded[..written]);
1781        crate::wipe_bytes(&mut encoded);
1782        result
1783    }
1784}
1785
1786const fn redacted_inner_state(present: bool) -> &'static str {
1787    if present { "<present>" } else { "<taken>" }
1788}