Skip to main content

base64/write/
encoder.rs

1use crate::engine::Engine;
2use std::{
3    cmp, fmt, io,
4    io::{ErrorKind, Result},
5};
6
7pub(crate) const BUF_SIZE: usize = 1024;
8/// The most bytes whose encoding will fit in `BUF_SIZE`
9const MAX_INPUT_LEN: usize = BUF_SIZE / 4 * 3;
10// 3 bytes of input = 4 bytes of base64, always (because we don't allow line wrapping)
11const MIN_ENCODE_CHUNK_SIZE: usize = 3;
12
13/// A `Write` implementation that base64 encodes data before delegating to the wrapped writer.
14///
15/// Because base64 has special handling for the end of the input data (padding, etc), there's a
16/// `finish()` method on this type that encodes any leftover input bytes and adds padding if
17/// appropriate. It's called automatically when deallocated (see the `Drop` implementation), but
18/// any error that occurs when invoking the underlying writer will be suppressed. If you want to
19/// handle such errors, call `finish()` yourself.
20///
21/// # Examples
22///
23/// ```
24/// use std::io::Write;
25/// use base64::engine::general_purpose;
26///
27/// // use a vec as the simplest possible `Write` -- in real code this is probably a file, etc.
28/// let mut enc = base64::write::EncoderWriter::new(Vec::new(), &general_purpose::STANDARD);
29///
30/// // handle errors as you normally would
31/// enc.write_all(b"asdf").unwrap();
32///
33/// // could leave this out to be called by Drop, if you don't care
34/// // about handling errors or getting the delegate writer back
35/// let delegate = enc.finish().unwrap();
36///
37/// // base64 was written to the writer
38/// assert_eq!(b"YXNkZg==", &delegate[..]);
39///
40/// ```
41///
42/// # Panics
43///
44/// Calling `write()` (or related methods) or `finish()` after `finish()` has completed without
45/// error is invalid and will panic.
46///
47/// # Errors
48///
49/// Base64 encoding itself does not generate errors, but errors from the wrapped writer will be
50/// returned as per the contract of `Write`.
51///
52/// # Performance
53///
54/// It has some minor performance loss compared to encoding slices (a couple percent).
55/// It does not do any heap allocation.
56///
57/// # Limitations
58///
59/// Owing to the specification of the `write` and `flush` methods on the `Write` trait and their
60/// implications for a buffering implementation, these methods may not behave as expected. In
61/// particular, calling `write_all` on this interface may fail with `io::ErrorKind::WriteZero`.
62/// See the documentation of the `Write` trait implementation for further details.
63pub struct EncoderWriter<'e, E: Engine, W: io::Write> {
64    engine: &'e E,
65    /// Where encoded data is written to. It's an Option as it's None immediately before Drop is
66    /// called so that `finish()` can return the underlying writer. None implies that `finish()` has
67    /// been called successfully.
68    delegate: Option<W>,
69    /// Holds a partial chunk, if any, after the last `write()`, so that we may then fill the chunk
70    /// with the next `write()`, encode it, then proceed with the rest of the input normally.
71    extra_input: [u8; MIN_ENCODE_CHUNK_SIZE],
72    /// How much of `extra` is occupied, in `[0, MIN_ENCODE_CHUNK_SIZE]`.
73    extra_input_occupied_len: usize,
74    /// Buffer to encode into. May hold leftover encoded bytes from a previous write call that the underlying writer
75    /// did not write last time.
76    output: [u8; BUF_SIZE],
77    /// How much of `output` is occupied with encoded data that couldn't be written last time
78    output_occupied_len: usize,
79    /// panic safety: don't write again in destructor if writer panicked while we were writing to it
80    panicked: bool,
81}
82
83impl<'e, E: Engine, W: io::Write> fmt::Debug for EncoderWriter<'e, E, W> {
84    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85        write!(
86            f,
87            "extra_input: {:?} extra_input_occupied_len:{:?} output[..5]: {:?} output_occupied_len: {:?}",
88            self.extra_input,
89            self.extra_input_occupied_len,
90            &self.output[0..5],
91            self.output_occupied_len
92        )
93    }
94}
95
96impl<'e, E: Engine, W: io::Write> EncoderWriter<'e, E, W> {
97    /// Create a new encoder that will write to the provided delegate writer.
98    pub fn new(delegate: W, engine: &'e E) -> EncoderWriter<'e, E, W> {
99        EncoderWriter {
100            engine,
101            delegate: Some(delegate),
102            extra_input: [0u8; MIN_ENCODE_CHUNK_SIZE],
103            extra_input_occupied_len: 0,
104            output: [0u8; BUF_SIZE],
105            output_occupied_len: 0,
106            panicked: false,
107        }
108    }
109
110    /// Encode all remaining buffered data and write it, including any trailing incomplete input
111    /// triples and associated padding.
112    ///
113    /// Once this succeeds, no further writes or calls to this method are allowed.
114    ///
115    /// This may write to the delegate writer multiple times if the delegate writer does not accept
116    /// all input provided to its `write` each invocation.
117    ///
118    /// If you don't care about error handling, it is not necessary to call this function, as the
119    /// equivalent finalization is done by the Drop impl.
120    ///
121    /// Returns the writer that this was constructed around.
122    ///
123    /// # Errors
124    ///
125    /// The first error that is not of `ErrorKind::Interrupted` will be returned.
126    pub fn finish(&mut self) -> Result<W> {
127        // If we could consume self in finish(), we wouldn't have to worry about this case, but
128        // finish() is retryable in the face of I/O errors, so we can't consume here.
129        assert!(
130            self.delegate.is_some(),
131            "Encoder has already had finish() called"
132        );
133
134        self.write_final_leftovers()?;
135
136        let writer = self.delegate.take().expect("Writer must be present");
137
138        Ok(writer)
139    }
140
141    /// Write any remaining buffered data to the delegate writer.
142    fn write_final_leftovers(&mut self) -> Result<()> {
143        if self.delegate.is_none() {
144            // finish() has already successfully called this, and we are now in drop() with a None
145            // writer, so just no-op
146            return Ok(());
147        }
148
149        self.write_all_encoded_output()?;
150
151        if self.extra_input_occupied_len > 0 {
152            let encoded_len = self
153                .engine
154                .encode_slice(
155                    &self.extra_input[..self.extra_input_occupied_len],
156                    &mut self.output[..],
157                )
158                .expect("buffer is large enough");
159
160            self.output_occupied_len = encoded_len;
161
162            self.write_all_encoded_output()?;
163
164            // write succeeded, do not write the encoding of extra again if finish() is retried
165            self.extra_input_occupied_len = 0;
166        }
167
168        Ok(())
169    }
170
171    /// Write as much of the encoded output to the delegate writer as it will accept, and store the
172    /// leftovers to be attempted at the next `write()` call. Updates `self.output_occupied_len`.
173    ///
174    /// # Errors
175    ///
176    /// Errors from the delegate writer are returned. In the case of an error,
177    /// `self.output_occupied_len` will not be updated, as errors from `write` are specified to mean
178    /// that no write took place.
179    fn write_to_delegate(&mut self, current_output_len: usize) -> Result<()> {
180        self.panicked = true;
181        let res = self
182            .delegate
183            .as_mut()
184            .expect("Writer must be present")
185            .write(&self.output[..current_output_len]);
186        self.panicked = false;
187
188        res.map(|consumed| {
189            debug_assert!(consumed <= current_output_len);
190
191            if consumed < current_output_len {
192                self.output_occupied_len = current_output_len.checked_sub(consumed).unwrap();
193                // If we're blocking on I/O, the minor inefficiency of copying bytes to the
194                // start of the buffer is the least of our concerns...
195                // TODO Rotate moves more than we need to; copy_within now stable.
196                self.output.rotate_left(consumed);
197            } else {
198                self.output_occupied_len = 0;
199            }
200        })
201    }
202
203    /// Write all buffered encoded output. If this returns `Ok`, `self.output_occupied_len` is `0`.
204    ///
205    /// This is basically `write_all` for the remaining buffered data but without the undesirable
206    /// abort-on-`Ok(0)` behavior.
207    ///
208    /// # Errors
209    ///
210    /// Any error emitted by the delegate writer abort the write loop and is returned, unless it's
211    /// `Interrupted`, in which case the error is ignored and writes will continue.
212    fn write_all_encoded_output(&mut self) -> Result<()> {
213        while self.output_occupied_len > 0 {
214            let remaining_len = self.output_occupied_len;
215            match self.write_to_delegate(remaining_len) {
216                // try again on interrupts ala write_all
217                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
218                // other errors return
219                Err(e) => return Err(e),
220                // success no-ops because remaining length is already updated
221                Ok(()) => {}
222            };
223        }
224
225        debug_assert_eq!(0, self.output_occupied_len);
226        Ok(())
227    }
228
229    /// Unwraps this `EncoderWriter`, returning the base writer it writes base64 encoded output
230    /// to.
231    ///
232    /// Normally this method should not be needed, since `finish()` returns the inner writer if
233    /// it completes successfully. That will also ensure all data has been flushed, which the
234    /// `into_inner()` function does *not* do.
235    ///
236    /// Calling this method after `finish()` has completed successfully will panic, since the
237    /// writer has already been returned.
238    ///
239    /// This method may be useful if the writer implements additional APIs beyond the `Write`
240    /// trait. Note that the inner writer might be in an error state or have an incomplete
241    /// base64 string written to it.
242    pub fn into_inner(mut self) -> W {
243        self.delegate
244            .take()
245            .expect("Encoder has already had finish() called")
246    }
247}
248
249impl<'e, E: Engine, W: io::Write> io::Write for EncoderWriter<'e, E, W> {
250    /// Encode input and then write to the delegate writer.
251    ///
252    /// Under non-error circumstances, this returns `Ok` with the value being the number of bytes
253    /// of `input` consumed. The value may be `0`, which interacts poorly with `write_all`, which
254    /// interprets `Ok(0)` as an error, despite it being allowed by the contract of `write`. See
255    /// <https://github.com/rust-lang/rust/issues/56889> for more on that.
256    ///
257    /// If the previous call to `write` provided more (encoded) data than the delegate writer could
258    /// accept in a single call to its `write`, the remaining data is buffered. As long as buffered
259    /// data is present, subsequent calls to `write` will try to write the remaining buffered data
260    /// to the delegate and return either `Ok(0)` -- and therefore not consume any of `input` -- or
261    /// an error.
262    ///
263    /// # Errors
264    ///
265    /// Any errors emitted by the delegate writer are returned.
266    fn write(&mut self, input: &[u8]) -> Result<usize> {
267        assert!(
268            self.delegate.is_some(),
269            "Cannot write more after calling finish()"
270        );
271
272        if input.is_empty() {
273            return Ok(0);
274        }
275
276        // The contract of `Write::write` places some constraints on this implementation:
277        // - a call to `write()` represents at most one call to a wrapped `Write`, so we can't
278        // iterate over the input and encode multiple chunks.
279        // - Errors mean that "no bytes were written to this writer", so we need to reset the
280        // internal state to what it was before the error occurred
281
282        // before reading any input, write any leftover encoded output from last time
283        if self.output_occupied_len > 0 {
284            let current_len = self.output_occupied_len;
285            return self
286                .write_to_delegate(current_len)
287                // did not read any input
288                .map(|()| 0);
289        }
290
291        debug_assert_eq!(0, self.output_occupied_len);
292
293        // how many bytes, if any, were read into `extra` to create a triple to encode
294        let mut extra_input_read_len = 0;
295        let mut input = input;
296
297        let orig_extra_len = self.extra_input_occupied_len;
298
299        let mut encoded_size = 0;
300        // always a multiple of MIN_ENCODE_CHUNK_SIZE
301        let mut max_input_len = MAX_INPUT_LEN;
302
303        // process leftover un-encoded input from last write
304        if self.extra_input_occupied_len > 0 {
305            debug_assert!(self.extra_input_occupied_len < 3);
306            if input.len() + self.extra_input_occupied_len >= MIN_ENCODE_CHUNK_SIZE {
307                // Fill up `extra`, encode that into `output`, and consume as much of the rest of
308                // `input` as possible.
309                // We could write just the encoding of `extra` by itself but then we'd have to
310                // return after writing only 4 bytes, which is inefficient if the underlying writer
311                // would make a syscall.
312                extra_input_read_len = MIN_ENCODE_CHUNK_SIZE - self.extra_input_occupied_len;
313                debug_assert!(extra_input_read_len > 0);
314                // overwrite only bytes that weren't already used. If we need to rollback extra_len
315                // (when the subsequent write errors), the old leading bytes will still be there.
316                self.extra_input[self.extra_input_occupied_len..MIN_ENCODE_CHUNK_SIZE]
317                    .copy_from_slice(&input[0..extra_input_read_len]);
318
319                let len = self.engine.internal_encode(
320                    &self.extra_input[0..MIN_ENCODE_CHUNK_SIZE],
321                    &mut self.output[..],
322                );
323                debug_assert_eq!(4, len);
324
325                input = &input[extra_input_read_len..];
326
327                // consider extra to be used up, since we encoded it
328                self.extra_input_occupied_len = 0;
329                // don't clobber where we just encoded to
330                encoded_size = 4;
331                // and don't read more than can be encoded
332                max_input_len = MAX_INPUT_LEN - MIN_ENCODE_CHUNK_SIZE;
333
334            // fall through to normal encoding
335            } else {
336                // `extra` and `input` are non empty, but `|extra| + |input| < 3`, so there must be
337                // 1 byte in each.
338                debug_assert_eq!(1, input.len());
339                debug_assert_eq!(1, self.extra_input_occupied_len);
340
341                self.extra_input[self.extra_input_occupied_len] = input[0];
342                self.extra_input_occupied_len += 1;
343                return Ok(1);
344            };
345        } else if input.len() < MIN_ENCODE_CHUNK_SIZE {
346            // `extra` is empty, and `input` fits inside it
347            self.extra_input[0..input.len()].copy_from_slice(input);
348            self.extra_input_occupied_len = input.len();
349            return Ok(input.len());
350        };
351
352        // either 0 or 1 complete chunks encoded from extra
353        debug_assert!(encoded_size == 0 || encoded_size == 4);
354        debug_assert!(
355            // didn't encode extra input
356            MAX_INPUT_LEN == max_input_len
357                // encoded one triple
358                || MAX_INPUT_LEN == max_input_len + MIN_ENCODE_CHUNK_SIZE
359        );
360
361        // encode complete triples only
362        let input_complete_chunks_len = input.len() - (input.len() % MIN_ENCODE_CHUNK_SIZE);
363        let input_chunks_to_encode_len = cmp::min(input_complete_chunks_len, max_input_len);
364        debug_assert_eq!(0, max_input_len % MIN_ENCODE_CHUNK_SIZE);
365        debug_assert_eq!(0, input_chunks_to_encode_len % MIN_ENCODE_CHUNK_SIZE);
366
367        encoded_size += self.engine.internal_encode(
368            &input[..(input_chunks_to_encode_len)],
369            &mut self.output[encoded_size..],
370        );
371
372        // not updating `self.output_occupied_len` here because if the below write fails, it should
373        // "never take place" -- the buffer contents we encoded are ignored and perhaps retried
374        // later, if the consumer chooses.
375
376        self.write_to_delegate(encoded_size)
377            // no matter whether we wrote the full encoded buffer or not, we consumed the same
378            // input
379            .map(|()| extra_input_read_len + input_chunks_to_encode_len)
380            .map_err(|e| {
381                // in case we filled and encoded `extra`, reset extra_len
382                self.extra_input_occupied_len = orig_extra_len;
383
384                e
385            })
386    }
387
388    /// Because this is usually treated as OK to call multiple times, it will *not* flush any
389    /// incomplete chunks of input or write padding.
390    /// # Errors
391    ///
392    /// The first error that is not of [`ErrorKind::Interrupted`] will be returned.
393    fn flush(&mut self) -> Result<()> {
394        self.write_all_encoded_output()?;
395        self.delegate
396            .as_mut()
397            .expect("Writer must be present")
398            .flush()
399    }
400}
401
402impl<'e, E: Engine, W: io::Write> Drop for EncoderWriter<'e, E, W> {
403    fn drop(&mut self) {
404        if !self.panicked {
405            // like `BufWriter`, ignore errors during drop
406            let _ = self.write_final_leftovers();
407        }
408    }
409}