@@ -46,18 +46,24 @@ pub mod stream {
4646 //! Streaming Base64 wrappers for `std::io`.
4747 //!
4848 //! ```
49- //! use std::io::Write;
50- //! use base64_ng::{STANDARD, stream::Encoder};
49+ //! use std::io::{Read, Write} ;
50+ //! use base64_ng::{STANDARD, stream::{ Encoder, EncoderReader} };
5151 //!
5252 //! let mut encoder = Encoder::new(Vec::new(), STANDARD);
5353 //! encoder.write_all(b"he").unwrap();
5454 //! encoder.write_all(b"llo").unwrap();
5555 //! let encoded = encoder.finish().unwrap();
5656 //! assert_eq!(encoded, b"aGVsbG8=");
57+ //!
58+ //! let mut reader = EncoderReader::new(&b"hello"[..], STANDARD);
59+ //! let mut encoded = String::new();
60+ //! reader.read_to_string(&mut encoded).unwrap();
61+ //! assert_eq!(encoded, "aGVsbG8=");
5762 //! ```
5863
5964 use super :: { Alphabet , EncodeError , Engine } ;
60- use std:: io:: { self , Write } ;
65+ use std:: collections:: VecDeque ;
66+ use std:: io:: { self , Read , Write } ;
6167
6268 /// A streaming Base64 encoder for `std::io::Write`.
6369 pub struct Encoder < W , A , const PAD : bool >
@@ -199,6 +205,148 @@ pub mod stream {
199205 fn encode_error_to_io ( err : EncodeError ) -> io:: Error {
200206 io:: Error :: new ( io:: ErrorKind :: InvalidInput , err)
201207 }
208+
209+ /// A streaming Base64 encoder for `std::io::Read`.
210+ pub struct EncoderReader < R , A , const PAD : bool >
211+ where
212+ A : Alphabet ,
213+ {
214+ inner : R ,
215+ engine : Engine < A , PAD > ,
216+ pending : [ u8 ; 2 ] ,
217+ pending_len : usize ,
218+ output : VecDeque < u8 > ,
219+ finished : bool ,
220+ }
221+
222+ impl < R , A , const PAD : bool > EncoderReader < R , A , PAD >
223+ where
224+ A : Alphabet ,
225+ {
226+ /// Creates a new streaming encoder reader.
227+ #[ must_use]
228+ pub fn new ( inner : R , engine : Engine < A , PAD > ) -> Self {
229+ Self {
230+ inner,
231+ engine,
232+ pending : [ 0 ; 2 ] ,
233+ pending_len : 0 ,
234+ output : VecDeque :: new ( ) ,
235+ finished : false ,
236+ }
237+ }
238+
239+ /// Returns a shared reference to the wrapped reader.
240+ #[ must_use]
241+ pub const fn get_ref ( & self ) -> & R {
242+ & self . inner
243+ }
244+
245+ /// Returns a mutable reference to the wrapped reader.
246+ pub fn get_mut ( & mut self ) -> & mut R {
247+ & mut self . inner
248+ }
249+
250+ /// Consumes the encoder reader and returns the wrapped reader.
251+ #[ must_use]
252+ pub fn into_inner ( self ) -> R {
253+ self . inner
254+ }
255+ }
256+
257+ impl < R , A , const PAD : bool > Read for EncoderReader < R , A , PAD >
258+ where
259+ R : Read ,
260+ A : Alphabet ,
261+ {
262+ fn read ( & mut self , output : & mut [ u8 ] ) -> io:: Result < usize > {
263+ if output. is_empty ( ) {
264+ return Ok ( 0 ) ;
265+ }
266+
267+ while self . output . is_empty ( ) && !self . finished {
268+ self . fill_output ( ) ?;
269+ }
270+
271+ let mut written = 0 ;
272+ while written < output. len ( ) {
273+ let Some ( byte) = self . output . pop_front ( ) else {
274+ break ;
275+ } ;
276+ output[ written] = byte;
277+ written += 1 ;
278+ }
279+
280+ Ok ( written)
281+ }
282+ }
283+
284+ impl < R , A , const PAD : bool > EncoderReader < R , A , PAD >
285+ where
286+ R : Read ,
287+ A : Alphabet ,
288+ {
289+ fn fill_output ( & mut self ) -> io:: Result < ( ) > {
290+ let mut input = [ 0u8 ; 768 ] ;
291+ let read = self . inner . read ( & mut input) ?;
292+ if read == 0 {
293+ self . finished = true ;
294+ self . push_final_pending ( ) ?;
295+ return Ok ( ( ) ) ;
296+ }
297+
298+ let mut consumed = 0 ;
299+ if self . pending_len > 0 {
300+ let needed = 3 - self . pending_len ;
301+ if read < needed {
302+ self . pending [ self . pending_len ..self . pending_len + read]
303+ . copy_from_slice ( & input[ ..read] ) ;
304+ self . pending_len += read;
305+ return Ok ( ( ) ) ;
306+ }
307+
308+ let mut chunk = [ 0u8 ; 3 ] ;
309+ chunk[ ..self . pending_len ] . copy_from_slice ( & self . pending [ ..self . pending_len ] ) ;
310+ chunk[ self . pending_len ..] . copy_from_slice ( & input[ ..needed] ) ;
311+ self . push_encoded ( & chunk) ?;
312+ self . pending_len = 0 ;
313+ consumed += needed;
314+ }
315+
316+ let remaining = & input[ consumed..read] ;
317+ let full_len = remaining. len ( ) / 3 * 3 ;
318+ if full_len > 0 {
319+ self . push_encoded ( & remaining[ ..full_len] ) ?;
320+ }
321+
322+ let tail = & remaining[ full_len..] ;
323+ self . pending [ ..tail. len ( ) ] . copy_from_slice ( tail) ;
324+ self . pending_len = tail. len ( ) ;
325+ Ok ( ( ) )
326+ }
327+
328+ fn push_final_pending ( & mut self ) -> io:: Result < ( ) > {
329+ if self . pending_len == 0 {
330+ return Ok ( ( ) ) ;
331+ }
332+
333+ let mut pending = [ 0u8 ; 2 ] ;
334+ pending[ ..self . pending_len ] . copy_from_slice ( & self . pending [ ..self . pending_len ] ) ;
335+ let pending_len = self . pending_len ;
336+ self . pending_len = 0 ;
337+ self . push_encoded ( & pending[ ..pending_len] )
338+ }
339+
340+ fn push_encoded ( & mut self , input : & [ u8 ] ) -> io:: Result < ( ) > {
341+ let mut encoded = [ 0u8 ; 1024 ] ;
342+ let written = self
343+ . engine
344+ . encode_slice ( input, & mut encoded)
345+ . map_err ( encode_error_to_io) ?;
346+ self . output . extend ( & encoded[ ..written] ) ;
347+ Ok ( ( ) )
348+ }
349+ }
202350}
203351
204352/// Standard Base64 engine with padding.
0 commit comments