base64/read/decoder.rs
1use crate::{engine::Engine, DecodeError, DecodeSliceError};
2use std::{cmp, fmt, io};
3
4// This should be large, but it has to fit on the stack.
5pub(crate) const BUF_SIZE: usize = 1024;
6
7// 4 bytes of base64 data encode 3 bytes of raw data (modulo padding).
8const BASE64_CHUNK_SIZE: usize = 4;
9const DECODED_CHUNK_SIZE: usize = 3;
10
11/// A `Read` implementation that decodes base64 data read from an underlying reader.
12///
13/// # Examples
14///
15/// ```
16/// use std::io::Read;
17/// use std::io::Cursor;
18/// use base64::engine::general_purpose;
19///
20/// // use a cursor as the simplest possible `Read` -- in real code this is probably a file, etc.
21/// let mut wrapped_reader = Cursor::new(b"YXNkZg==");
22/// let mut decoder = base64::read::DecoderReader::new(
23/// &mut wrapped_reader,
24/// &general_purpose::STANDARD);
25///
26/// // handle errors as you normally would
27/// let mut result = Vec::new();
28/// decoder.read_to_end(&mut result).unwrap();
29///
30/// assert_eq!(b"asdf", &result[..]);
31///
32/// ```
33pub struct DecoderReader<'e, E: Engine, R: io::Read> {
34 engine: &'e E,
35 /// Where b64 data is read from
36 inner: R,
37
38 /// Holds b64 data read from the delegate reader.
39 b64_buffer: [u8; BUF_SIZE],
40 /// The start of the pending buffered data in `b64_buffer`.
41 b64_offset: usize,
42 /// The amount of buffered b64 data after `b64_offset` in `b64_len`.
43 b64_len: usize,
44 /// Since the caller may provide us with a buffer of size 1 or 2 that's too small to copy a
45 /// decoded chunk in to, we have to be able to hang on to a few decoded bytes.
46 /// Technically we only need to hold 2 bytes, but then we'd need a separate temporary buffer to
47 /// decode 3 bytes into and then juggle copying one byte into the provided read buf and the rest
48 /// into here, which seems like a lot of complexity for 1 extra byte of storage.
49 decoded_chunk_buffer: [u8; DECODED_CHUNK_SIZE],
50 /// Index of start of decoded data in `decoded_chunk_buffer`
51 decoded_offset: usize,
52 /// Length of decoded data after `decoded_offset` in `decoded_chunk_buffer`
53 decoded_len: usize,
54 /// Input length consumed so far.
55 /// Used to provide accurate offsets in errors
56 input_consumed_len: usize,
57 /// offset of previously seen padding, if any
58 padding_offset: Option<usize>,
59}
60
61// exclude b64_buffer as it's uselessly large
62impl<'e, E: Engine, R: io::Read> fmt::Debug for DecoderReader<'e, E, R> {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 f.debug_struct("DecoderReader")
65 .field("b64_offset", &self.b64_offset)
66 .field("b64_len", &self.b64_len)
67 .field("decoded_chunk_buffer", &self.decoded_chunk_buffer)
68 .field("decoded_offset", &self.decoded_offset)
69 .field("decoded_len", &self.decoded_len)
70 .field("input_consumed_len", &self.input_consumed_len)
71 .field("padding_offset", &self.padding_offset)
72 .finish()
73 }
74}
75
76impl<'e, E: Engine, R: io::Read> DecoderReader<'e, E, R> {
77 /// Create a new decoder that will read from the provided reader `r`.
78 pub fn new(reader: R, engine: &'e E) -> Self {
79 DecoderReader {
80 engine,
81 inner: reader,
82 b64_buffer: [0; BUF_SIZE],
83 b64_offset: 0,
84 b64_len: 0,
85 decoded_chunk_buffer: [0; DECODED_CHUNK_SIZE],
86 decoded_offset: 0,
87 decoded_len: 0,
88 input_consumed_len: 0,
89 padding_offset: None,
90 }
91 }
92
93 /// Write as much as possible of the decoded buffer into the target buffer.
94 /// Must only be called when there is something to write and space to write into.
95 /// Returns a Result with the number of (decoded) bytes copied.
96 fn flush_decoded_buf(&mut self, buf: &mut [u8]) -> io::Result<usize> {
97 debug_assert!(self.decoded_len > 0);
98 debug_assert!(!buf.is_empty());
99
100 let copy_len = cmp::min(self.decoded_len, buf.len());
101 debug_assert!(copy_len > 0);
102 debug_assert!(copy_len <= self.decoded_len);
103
104 buf[..copy_len].copy_from_slice(
105 &self.decoded_chunk_buffer[self.decoded_offset..self.decoded_offset + copy_len],
106 );
107
108 self.decoded_offset += copy_len;
109 self.decoded_len -= copy_len;
110
111 debug_assert!(self.decoded_len < DECODED_CHUNK_SIZE);
112
113 Ok(copy_len)
114 }
115
116 /// Read into the remaining space in the buffer after the current contents.
117 /// Must only be called when there is space to read into in the buffer.
118 /// Returns the number of bytes read.
119 fn read_from_delegate(&mut self) -> io::Result<usize> {
120 debug_assert!(self.b64_offset + self.b64_len < BUF_SIZE);
121
122 let read = self
123 .inner
124 .read(&mut self.b64_buffer[self.b64_offset + self.b64_len..])?;
125 self.b64_len += read;
126
127 debug_assert!(self.b64_offset + self.b64_len <= BUF_SIZE);
128
129 Ok(read)
130 }
131
132 /// Decode the requested number of bytes from the b64 buffer into the provided buffer. It's the
133 /// caller's responsibility to choose the number of b64 bytes to decode correctly.
134 ///
135 /// Returns a Result with the number of decoded bytes written to `buf`.
136 ///
137 /// # Panics
138 ///
139 /// panics if `buf` is too small
140 fn decode_to_buf(&mut self, b64_len_to_decode: usize, buf: &mut [u8]) -> io::Result<usize> {
141 debug_assert!(self.b64_len >= b64_len_to_decode);
142 debug_assert!(self.b64_offset + self.b64_len <= BUF_SIZE);
143 debug_assert!(!buf.is_empty());
144
145 let b64_to_decode = &self.b64_buffer[self.b64_offset..self.b64_offset + b64_len_to_decode];
146 let decode_metadata = self
147 .engine
148 .internal_decode(
149 b64_to_decode,
150 buf,
151 self.engine.internal_decoded_len_estimate(b64_len_to_decode),
152 )
153 .map_err(|dse| match dse {
154 DecodeSliceError::DecodeError(de) => {
155 match de {
156 DecodeError::InvalidByte(offset, byte) => {
157 match (byte, self.padding_offset) {
158 // if there was padding in a previous block of decoding that happened to
159 // be correct, and we now find more padding that happens to be incorrect,
160 // to be consistent with non-reader decodes, record the error at the first
161 // padding
162 (byte, Some(first_pad_offset))
163 if byte == self.engine.padding().as_u8() =>
164 {
165 DecodeError::InvalidByte(
166 first_pad_offset,
167 self.engine.padding().as_u8(),
168 )
169 }
170 _ => {
171 DecodeError::InvalidByte(self.input_consumed_len + offset, byte)
172 }
173 }
174 }
175 DecodeError::InvalidLength(len) => {
176 DecodeError::InvalidLength(self.input_consumed_len + len)
177 }
178 DecodeError::InvalidLastSymbol {
179 offset,
180 symbol,
181 symbol_value,
182 } => DecodeError::InvalidLastSymbol {
183 offset: self.input_consumed_len + offset,
184 symbol,
185 symbol_value,
186 },
187 DecodeError::InvalidPadding => DecodeError::InvalidPadding,
188 }
189 }
190 DecodeSliceError::OutputSliceTooSmall => {
191 unreachable!("buf is sized correctly in calling code")
192 }
193 })
194 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
195
196 if let Some(offset) = self.padding_offset {
197 // we've already seen padding
198 if decode_metadata.decoded_len > 0 {
199 // we read more after already finding padding; report error at first padding byte
200 return Err(io::Error::new(
201 io::ErrorKind::InvalidData,
202 DecodeError::InvalidByte(offset, self.engine.padding().as_u8()),
203 ));
204 }
205 }
206
207 self.padding_offset = self.padding_offset.or(decode_metadata
208 .padding_offset
209 .map(|offset| self.input_consumed_len + offset));
210 self.input_consumed_len += b64_len_to_decode;
211 self.b64_offset += b64_len_to_decode;
212 self.b64_len -= b64_len_to_decode;
213
214 debug_assert!(self.b64_offset + self.b64_len <= BUF_SIZE);
215
216 Ok(decode_metadata.decoded_len)
217 }
218
219 /// Unwraps this `DecoderReader`, returning the base reader which it reads base64 encoded
220 /// input from.
221 ///
222 /// Because `DecoderReader` performs internal buffering, the state of the inner reader is
223 /// unspecified. This function is mainly provided because the inner reader type may provide
224 /// additional functionality beyond the `Read` implementation which may still be useful.
225 pub fn into_inner(self) -> R {
226 self.inner
227 }
228}
229
230impl<'e, E: Engine, R: io::Read> io::Read for DecoderReader<'e, E, R> {
231 /// Decode input from the wrapped reader.
232 ///
233 /// Under non-error circumstances, this returns `Ok` with the value being the number of bytes
234 /// written in `buf`.
235 ///
236 /// Where possible, this function buffers base64 to minimize the number of `read()` calls to the
237 /// delegate reader.
238 ///
239 /// # Errors
240 ///
241 /// Any errors emitted by the delegate reader are returned. Decoding errors due to invalid
242 /// base64 are also possible, and will have `io::ErrorKind::InvalidData`.
243 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
244 if buf.is_empty() {
245 return Ok(0);
246 }
247
248 // offset == BUF_SIZE when we copied it all last time
249 debug_assert!(self.b64_offset <= BUF_SIZE);
250 debug_assert!(self.b64_offset + self.b64_len <= BUF_SIZE);
251 debug_assert!(if self.b64_offset == BUF_SIZE {
252 self.b64_len == 0
253 } else {
254 self.b64_len <= BUF_SIZE
255 });
256
257 debug_assert!(if self.decoded_len == 0 {
258 // can be = when we were able to copy the complete chunk
259 self.decoded_offset <= DECODED_CHUNK_SIZE
260 } else {
261 self.decoded_offset < DECODED_CHUNK_SIZE
262 });
263
264 // We shouldn't ever decode into decoded_buffer when we can't immediately write at least one
265 // byte into the provided buf, so the effective length should only be 3 momentarily between
266 // when we decode and when we copy into the target buffer.
267 debug_assert!(self.decoded_len < DECODED_CHUNK_SIZE);
268 debug_assert!(self.decoded_len + self.decoded_offset <= DECODED_CHUNK_SIZE);
269
270 if self.decoded_len > 0 {
271 // we have a few leftover decoded bytes; flush that rather than pull in more b64
272 self.flush_decoded_buf(buf)
273 } else {
274 let mut at_eof = false;
275 while self.b64_len < BASE64_CHUNK_SIZE {
276 // Copy any bytes we have to the start of the buffer.
277 self.b64_buffer
278 .copy_within(self.b64_offset..self.b64_offset + self.b64_len, 0);
279 self.b64_offset = 0;
280
281 // then fill in more data
282 let read = self.read_from_delegate()?;
283 if read == 0 {
284 // we never read into an empty buf, so 0 => we've hit EOF
285 at_eof = true;
286 break;
287 }
288 }
289
290 if self.b64_len == 0 {
291 debug_assert!(at_eof);
292 // we must be at EOF, and we have no data left to decode
293 return Ok(0);
294 };
295
296 debug_assert!(if at_eof {
297 // if we are at eof, we may not have a complete chunk
298 self.b64_len > 0
299 } else {
300 // otherwise, we must have at least one chunk
301 self.b64_len >= BASE64_CHUNK_SIZE
302 });
303
304 debug_assert_eq!(0, self.decoded_len);
305
306 if buf.len() < DECODED_CHUNK_SIZE {
307 // caller requested an annoyingly short read
308 // have to write to a tmp buf first to avoid double mutable borrow
309 let mut decoded_chunk = [0_u8; DECODED_CHUNK_SIZE];
310 // if we are at eof, could have less than BASE64_CHUNK_SIZE, in which case we have
311 // to assume that these last few symbols are, in fact, valid (i.e. must be 2-4 b64
312 // symbols, not 1, since 1 symbols can't decode to 1 byte).
313 let to_decode = cmp::min(self.b64_len, BASE64_CHUNK_SIZE);
314
315 let decoded = self.decode_to_buf(to_decode, &mut decoded_chunk[..])?;
316 self.decoded_chunk_buffer[..decoded].copy_from_slice(&decoded_chunk[..decoded]);
317
318 self.decoded_offset = 0;
319 self.decoded_len = decoded;
320
321 // can be less than 3 on last block due to padding
322 debug_assert!(decoded <= 3);
323
324 self.flush_decoded_buf(buf)
325 } else {
326 let b64_bytes_that_can_decode_into_buf = (buf.len() / DECODED_CHUNK_SIZE)
327 .checked_mul(BASE64_CHUNK_SIZE)
328 .expect("too many chunks");
329 debug_assert!(b64_bytes_that_can_decode_into_buf >= BASE64_CHUNK_SIZE);
330
331 let b64_bytes_available_to_decode = if at_eof {
332 self.b64_len
333 } else {
334 // only use complete chunks
335 self.b64_len - self.b64_len % 4
336 };
337
338 let actual_decode_len = cmp::min(
339 b64_bytes_that_can_decode_into_buf,
340 b64_bytes_available_to_decode,
341 );
342 self.decode_to_buf(actual_decode_len, buf)
343 }
344 }
345 }
346}