/rust/registry/src/index.crates.io-1949cf8c6b5b557f/base64ct-1.8.0/src/decoder.rs
Line | Count | Source |
1 | | //! Buffered Base64 decoder. |
2 | | |
3 | | use crate::{ |
4 | | Encoding, |
5 | | Error::{self, InvalidLength}, |
6 | | MIN_LINE_WIDTH, encoding, |
7 | | line_ending::{CHAR_CR, CHAR_LF}, |
8 | | }; |
9 | | use core::{cmp, marker::PhantomData}; |
10 | | |
11 | | #[cfg(feature = "alloc")] |
12 | | use {alloc::vec::Vec, core::iter}; |
13 | | |
14 | | #[cfg(feature = "std")] |
15 | | use std::io; |
16 | | |
17 | | #[cfg(doc)] |
18 | | use crate::{Base64, Base64Unpadded}; |
19 | | |
20 | | /// Stateful Base64 decoder with support for buffered, incremental decoding. |
21 | | /// |
22 | | /// The `E` type parameter can be any type which impls [`Encoding`] such as |
23 | | /// [`Base64`] or [`Base64Unpadded`]. |
24 | | #[derive(Clone)] |
25 | | pub struct Decoder<'i, E: Encoding> { |
26 | | /// Current line being processed. |
27 | | line: Line<'i>, |
28 | | |
29 | | /// Base64 input data reader. |
30 | | line_reader: LineReader<'i>, |
31 | | |
32 | | /// Length of the remaining data after Base64 decoding. |
33 | | remaining_len: usize, |
34 | | |
35 | | /// Block buffer used for non-block-aligned data. |
36 | | block_buffer: BlockBuffer, |
37 | | |
38 | | /// Phantom parameter for the Base64 encoding in use. |
39 | | encoding: PhantomData<E>, |
40 | | } |
41 | | |
42 | | impl<'i, E: Encoding> Decoder<'i, E> { |
43 | | /// Create a new decoder for a byte slice containing contiguous |
44 | | /// (non-newline-delimited) Base64-encoded data. |
45 | | /// |
46 | | /// # Returns |
47 | | /// - `Ok(decoder)` on success. |
48 | | /// - `Err(Error::InvalidLength)` if the input buffer is empty. |
49 | 0 | pub fn new(input: &'i [u8]) -> Result<Self, Error> { |
50 | 0 | let line_reader = LineReader::new_unwrapped(input)?; |
51 | 0 | let remaining_len = line_reader.decoded_len::<E>()?; |
52 | | |
53 | 0 | Ok(Self { |
54 | 0 | line: Line::default(), |
55 | 0 | line_reader, |
56 | 0 | remaining_len, |
57 | 0 | block_buffer: BlockBuffer::default(), |
58 | 0 | encoding: PhantomData, |
59 | 0 | }) |
60 | 0 | } |
61 | | |
62 | | /// Create a new decoder for a byte slice containing Base64 which |
63 | | /// line wraps at the given line length. |
64 | | /// |
65 | | /// Trailing newlines are not supported and must be removed in advance. |
66 | | /// |
67 | | /// Newlines are handled according to what are roughly [RFC7468] conventions: |
68 | | /// |
69 | | /// ```text |
70 | | /// [parsers] MUST handle different newline conventions |
71 | | /// ``` |
72 | | /// |
73 | | /// RFC7468 allows any of the following as newlines, and allows a mixture |
74 | | /// of different types of newlines: |
75 | | /// |
76 | | /// ```text |
77 | | /// eol = CRLF / CR / LF |
78 | | /// ``` |
79 | | /// |
80 | | /// # Returns |
81 | | /// - `Ok(decoder)` on success. |
82 | | /// - `Err(Error::InvalidLength)` if the input buffer is empty or the line |
83 | | /// width is zero. |
84 | | /// |
85 | | /// [RFC7468]: https://datatracker.ietf.org/doc/html/rfc7468 |
86 | 0 | pub fn new_wrapped(input: &'i [u8], line_width: usize) -> Result<Self, Error> { |
87 | 0 | let line_reader = LineReader::new_wrapped(input, line_width)?; |
88 | 0 | let remaining_len = line_reader.decoded_len::<E>()?; |
89 | | |
90 | 0 | Ok(Self { |
91 | 0 | line: Line::default(), |
92 | 0 | line_reader, |
93 | 0 | remaining_len, |
94 | 0 | block_buffer: BlockBuffer::default(), |
95 | 0 | encoding: PhantomData, |
96 | 0 | }) |
97 | 0 | } |
98 | | |
99 | | /// Fill the provided buffer with data decoded from Base64. |
100 | | /// |
101 | | /// Enough Base64 input data must remain to fill the entire buffer. |
102 | | /// |
103 | | /// # Returns |
104 | | /// - `Ok(bytes)` if the expected amount of data was read |
105 | | /// - `Err(Error::InvalidLength)` if the exact amount of data couldn't be read |
106 | 0 | pub fn decode<'o>(&mut self, out: &'o mut [u8]) -> Result<&'o [u8], Error> { |
107 | 0 | if self.is_finished() && !out.is_empty() { |
108 | 0 | return Err(InvalidLength); |
109 | 0 | } |
110 | | |
111 | 0 | let mut out_pos = 0; |
112 | | |
113 | 0 | while out_pos < out.len() { |
114 | | // If there's data in the block buffer, use it |
115 | 0 | if !self.block_buffer.is_empty() { |
116 | 0 | let out_rem = out.len().checked_sub(out_pos).ok_or(InvalidLength)?; |
117 | 0 | let bytes = self.block_buffer.take(out_rem)?; |
118 | 0 | out[out_pos..][..bytes.len()].copy_from_slice(bytes); |
119 | 0 | out_pos = out_pos.checked_add(bytes.len()).ok_or(InvalidLength)?; |
120 | 0 | } |
121 | | |
122 | | // Advance the line reader if necessary |
123 | 0 | if self.line.is_empty() && !self.line_reader.is_empty() { |
124 | 0 | self.advance_line()?; |
125 | 0 | } |
126 | | |
127 | | // Attempt to decode a stride of block-aligned data |
128 | 0 | let in_blocks = self.line.len() / 4; |
129 | 0 | let out_rem = out.len().checked_sub(out_pos).ok_or(InvalidLength)?; |
130 | 0 | let out_blocks = out_rem / 3; |
131 | 0 | let blocks = cmp::min(in_blocks, out_blocks); |
132 | 0 | let in_aligned = self.line.take(blocks.checked_mul(4).ok_or(InvalidLength)?); |
133 | | |
134 | 0 | if !in_aligned.is_empty() { |
135 | 0 | let out_buf = &mut out[out_pos..][..blocks.checked_mul(3).ok_or(InvalidLength)?]; |
136 | 0 | let decoded_len = self.perform_decode(in_aligned, out_buf)?.len(); |
137 | 0 | out_pos = out_pos.checked_add(decoded_len).ok_or(InvalidLength)?; |
138 | 0 | } |
139 | | |
140 | 0 | if out_pos < out.len() { |
141 | 0 | if self.is_finished() { |
142 | | // If we're out of input then we've been requested to decode |
143 | | // more data than is actually available. |
144 | 0 | return Err(InvalidLength); |
145 | | } else { |
146 | | // If we still have data available but haven't completely |
147 | | // filled the output slice, we're in a situation where |
148 | | // either the input or output isn't block-aligned, so fill |
149 | | // the internal block buffer. |
150 | 0 | self.fill_block_buffer()?; |
151 | | } |
152 | 0 | } |
153 | | } |
154 | | |
155 | 0 | self.remaining_len = self |
156 | 0 | .remaining_len |
157 | 0 | .checked_sub(out.len()) |
158 | 0 | .ok_or(InvalidLength)?; |
159 | | |
160 | 0 | Ok(out) |
161 | 0 | } |
162 | | |
163 | | /// Decode all remaining Base64 data, placing the result into `buf`. |
164 | | /// |
165 | | /// If successful, this function will return the total number of bytes |
166 | | /// decoded into `buf`. |
167 | | #[cfg(feature = "alloc")] |
168 | | pub fn decode_to_end<'o>(&mut self, buf: &'o mut Vec<u8>) -> Result<&'o [u8], Error> { |
169 | | let start_len = buf.len(); |
170 | | let remaining_len = self.remaining_len(); |
171 | | let total_len = start_len.checked_add(remaining_len).ok_or(InvalidLength)?; |
172 | | |
173 | | if total_len > buf.capacity() { |
174 | | buf.reserve(total_len.checked_sub(buf.capacity()).ok_or(InvalidLength)?); |
175 | | } |
176 | | |
177 | | // Append `decoded_len` zeroes to the vector |
178 | | buf.extend(iter::repeat_n(0, remaining_len)); |
179 | | self.decode(&mut buf[start_len..])?; |
180 | | Ok(&buf[start_len..]) |
181 | | } |
182 | | |
183 | | /// Get the length of the remaining data after Base64 decoding. |
184 | | /// |
185 | | /// Decreases every time data is decoded. |
186 | 0 | pub fn remaining_len(&self) -> usize { |
187 | 0 | self.remaining_len |
188 | 0 | } |
189 | | |
190 | | /// Has all of the input data been decoded? |
191 | 0 | pub fn is_finished(&self) -> bool { |
192 | 0 | self.line.is_empty() && self.line_reader.is_empty() && self.block_buffer.is_empty() |
193 | 0 | } |
194 | | |
195 | | /// Fill the block buffer with data. |
196 | 0 | fn fill_block_buffer(&mut self) -> Result<(), Error> { |
197 | 0 | let mut buf = [0u8; BlockBuffer::SIZE]; |
198 | | |
199 | 0 | let decoded = if self.line.len() < 4 && !self.line_reader.is_empty() { |
200 | | // Handle input block which is split across lines |
201 | 0 | let mut tmp = [0u8; 4]; |
202 | | |
203 | | // Copy remaining data in the line into tmp |
204 | 0 | let line_end = self.line.take(4); |
205 | 0 | tmp[..line_end.len()].copy_from_slice(line_end); |
206 | | |
207 | | // Advance the line and attempt to fill tmp |
208 | 0 | self.advance_line()?; |
209 | 0 | let len = 4usize.checked_sub(line_end.len()).ok_or(InvalidLength)?; |
210 | 0 | let line_begin = self.line.take(len); |
211 | 0 | tmp[line_end.len()..][..line_begin.len()].copy_from_slice(line_begin); |
212 | | |
213 | 0 | let tmp_len = line_begin |
214 | 0 | .len() |
215 | 0 | .checked_add(line_end.len()) |
216 | 0 | .ok_or(InvalidLength)?; |
217 | | |
218 | 0 | self.perform_decode(&tmp[..tmp_len], &mut buf) |
219 | | } else { |
220 | 0 | let block = self.line.take(4); |
221 | 0 | self.perform_decode(block, &mut buf) |
222 | 0 | }?; |
223 | | |
224 | 0 | self.block_buffer.fill(decoded) |
225 | 0 | } |
226 | | |
227 | | /// Advance the internal buffer to the next line. |
228 | 0 | fn advance_line(&mut self) -> Result<(), Error> { |
229 | 0 | debug_assert!(self.line.is_empty(), "expected line buffer to be empty"); |
230 | | |
231 | 0 | if let Some(line) = self.line_reader.next().transpose()? { |
232 | 0 | self.line = line; |
233 | 0 | Ok(()) |
234 | | } else { |
235 | 0 | Err(InvalidLength) |
236 | | } |
237 | 0 | } |
238 | | |
239 | | /// Perform Base64 decoding operation. |
240 | 0 | fn perform_decode<'o>(&self, src: &[u8], dst: &'o mut [u8]) -> Result<&'o [u8], Error> { |
241 | 0 | if self.is_finished() { |
242 | 0 | E::decode(src, dst) |
243 | | } else { |
244 | 0 | E::Unpadded::decode(src, dst) |
245 | | } |
246 | 0 | } |
247 | | } |
248 | | |
249 | | #[cfg(feature = "std")] |
250 | | impl<E: Encoding> io::Read for Decoder<'_, E> { |
251 | | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
252 | | if self.is_finished() { |
253 | | return Ok(0); |
254 | | } |
255 | | let slice = match buf.get_mut(..self.remaining_len()) { |
256 | | Some(bytes) => bytes, |
257 | | None => buf, |
258 | | }; |
259 | | |
260 | | self.decode(slice)?; |
261 | | Ok(slice.len()) |
262 | | } |
263 | | |
264 | | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
265 | | if self.is_finished() { |
266 | | return Ok(0); |
267 | | } |
268 | | Ok(self.decode_to_end(buf)?.len()) |
269 | | } |
270 | | |
271 | | fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { |
272 | | self.decode(buf)?; |
273 | | Ok(()) |
274 | | } |
275 | | } |
276 | | |
277 | | /// Base64 decode buffer for a 1-block input. |
278 | | /// |
279 | | /// This handles a partially decoded block of data, i.e. data which has been |
280 | | /// decoded but not read. |
281 | | #[derive(Clone, Default, Debug)] |
282 | | struct BlockBuffer { |
283 | | /// 3 decoded bytes from a 4-byte Base64-encoded input. |
284 | | decoded: [u8; Self::SIZE], |
285 | | |
286 | | /// Length of the buffer. |
287 | | length: usize, |
288 | | |
289 | | /// Position within the buffer. |
290 | | position: usize, |
291 | | } |
292 | | |
293 | | impl BlockBuffer { |
294 | | /// Size of the buffer in bytes. |
295 | | const SIZE: usize = 3; |
296 | | |
297 | | /// Fill the buffer by decoding up to 3 bytes of decoded Base64 input. |
298 | 0 | fn fill(&mut self, decoded_input: &[u8]) -> Result<(), Error> { |
299 | 0 | debug_assert!(self.is_empty()); |
300 | | |
301 | 0 | if decoded_input.len() > Self::SIZE { |
302 | 0 | return Err(InvalidLength); |
303 | 0 | } |
304 | | |
305 | 0 | self.position = 0; |
306 | 0 | self.length = decoded_input.len(); |
307 | 0 | self.decoded[..decoded_input.len()].copy_from_slice(decoded_input); |
308 | 0 | Ok(()) |
309 | 0 | } |
310 | | |
311 | | /// Take a specified number of bytes from the buffer. |
312 | | /// |
313 | | /// Returns as many bytes as possible, or an empty slice if the buffer has |
314 | | /// already been read to completion. |
315 | 0 | fn take(&mut self, mut nbytes: usize) -> Result<&[u8], Error> { |
316 | 0 | debug_assert!(self.position <= self.length); |
317 | 0 | let start_pos = self.position; |
318 | 0 | let remaining_len = self.length.checked_sub(start_pos).ok_or(InvalidLength)?; |
319 | | |
320 | 0 | if nbytes > remaining_len { |
321 | 0 | nbytes = remaining_len; |
322 | 0 | } |
323 | | |
324 | 0 | self.position = self.position.checked_add(nbytes).ok_or(InvalidLength)?; |
325 | 0 | Ok(&self.decoded[start_pos..][..nbytes]) |
326 | 0 | } |
327 | | |
328 | | /// Have all of the bytes in this buffer been consumed? |
329 | 0 | fn is_empty(&self) -> bool { |
330 | 0 | self.position == self.length |
331 | 0 | } |
332 | | } |
333 | | |
334 | | /// A single line of linewrapped data, providing a read buffer. |
335 | | #[derive(Clone, Debug)] |
336 | | pub struct Line<'i> { |
337 | | /// Remaining data in the line |
338 | | remaining: &'i [u8], |
339 | | } |
340 | | |
341 | | impl Default for Line<'_> { |
342 | 0 | fn default() -> Self { |
343 | 0 | Self::new(&[]) |
344 | 0 | } |
345 | | } |
346 | | |
347 | | impl<'i> Line<'i> { |
348 | | /// Create a new line which wraps the given input data. |
349 | 0 | fn new(bytes: &'i [u8]) -> Self { |
350 | 0 | Self { remaining: bytes } |
351 | 0 | } |
352 | | |
353 | | /// Take up to `nbytes` from this line buffer. |
354 | 0 | fn take(&mut self, nbytes: usize) -> &'i [u8] { |
355 | 0 | let (bytes, rest) = if nbytes < self.remaining.len() { |
356 | 0 | self.remaining.split_at(nbytes) |
357 | | } else { |
358 | 0 | (self.remaining, [].as_ref()) |
359 | | }; |
360 | | |
361 | 0 | self.remaining = rest; |
362 | 0 | bytes |
363 | 0 | } |
364 | | |
365 | | /// Slice off a tail of a given length. |
366 | 0 | fn slice_tail(&self, nbytes: usize) -> Result<&'i [u8], Error> { |
367 | 0 | let offset = self.len().checked_sub(nbytes).ok_or(InvalidLength)?; |
368 | 0 | self.remaining.get(offset..).ok_or(InvalidLength) |
369 | 0 | } |
370 | | |
371 | | /// Get the number of bytes remaining in this line. |
372 | 0 | fn len(&self) -> usize { |
373 | 0 | self.remaining.len() |
374 | 0 | } |
375 | | |
376 | | /// Is the buffer for this line empty? |
377 | 0 | fn is_empty(&self) -> bool { |
378 | 0 | self.len() == 0 |
379 | 0 | } |
380 | | |
381 | | /// Trim the newline off the end of this line. |
382 | 0 | fn trim_end(&self) -> Self { |
383 | 0 | Line::new(match self.remaining { |
384 | 0 | [line @ .., CHAR_CR, CHAR_LF] => line, |
385 | 0 | [line @ .., CHAR_CR] => line, |
386 | 0 | [line @ .., CHAR_LF] => line, |
387 | 0 | line => line, |
388 | | }) |
389 | 0 | } |
390 | | } |
391 | | |
392 | | /// Iterator over multi-line Base64 input. |
393 | | #[derive(Clone)] |
394 | | struct LineReader<'i> { |
395 | | /// Remaining linewrapped data to be processed. |
396 | | remaining: &'i [u8], |
397 | | |
398 | | /// Line width. |
399 | | line_width: Option<usize>, |
400 | | } |
401 | | |
402 | | impl<'i> LineReader<'i> { |
403 | | /// Create a new reader which operates over continugous unwrapped data. |
404 | 0 | fn new_unwrapped(bytes: &'i [u8]) -> Result<Self, Error> { |
405 | 0 | if bytes.is_empty() { |
406 | 0 | Err(InvalidLength) |
407 | | } else { |
408 | 0 | Ok(Self { |
409 | 0 | remaining: bytes, |
410 | 0 | line_width: None, |
411 | 0 | }) |
412 | | } |
413 | 0 | } |
414 | | |
415 | | /// Create a new reader which operates over linewrapped data. |
416 | 0 | fn new_wrapped(bytes: &'i [u8], line_width: usize) -> Result<Self, Error> { |
417 | 0 | if line_width < MIN_LINE_WIDTH { |
418 | 0 | return Err(InvalidLength); |
419 | 0 | } |
420 | | |
421 | 0 | let mut reader = Self::new_unwrapped(bytes)?; |
422 | 0 | reader.line_width = Some(line_width); |
423 | 0 | Ok(reader) |
424 | 0 | } |
425 | | |
426 | | /// Is this line reader empty? |
427 | 0 | fn is_empty(&self) -> bool { |
428 | 0 | self.remaining.is_empty() |
429 | 0 | } |
430 | | |
431 | | /// Get the total length of the data decoded from this line reader. |
432 | 0 | fn decoded_len<E: Encoding>(&self) -> Result<usize, Error> { |
433 | 0 | let mut buffer = [0u8; 4]; |
434 | 0 | let mut lines = self.clone(); |
435 | 0 | let mut line = match lines.next().transpose()? { |
436 | 0 | Some(l) => l, |
437 | 0 | None => return Ok(0), |
438 | | }; |
439 | 0 | let mut base64_len = 0usize; |
440 | | |
441 | | loop { |
442 | 0 | base64_len = base64_len.checked_add(line.len()).ok_or(InvalidLength)?; |
443 | | |
444 | 0 | match lines.next().transpose()? { |
445 | 0 | Some(l) => { |
446 | | // Store the end of the line in the buffer so we can |
447 | | // reassemble the last block to determine the real length |
448 | 0 | buffer.copy_from_slice(line.slice_tail(4)?); |
449 | | |
450 | 0 | line = l |
451 | | } |
452 | | |
453 | | // To compute an exact decoded length we need to decode the |
454 | | // last Base64 block and get the decoded length. |
455 | | // |
456 | | // This is what the somewhat complex code below is doing. |
457 | | None => { |
458 | | // Compute number of bytes in the last block (may be unpadded) |
459 | 0 | let base64_last_block_len = match base64_len % 4 { |
460 | 0 | 0 => 4, |
461 | 0 | n => n, |
462 | | }; |
463 | | |
464 | | // Compute decoded length without the last block |
465 | 0 | let decoded_len = encoding::decoded_len( |
466 | 0 | base64_len |
467 | 0 | .checked_sub(base64_last_block_len) |
468 | 0 | .ok_or(InvalidLength)?, |
469 | | ); |
470 | | |
471 | | // Compute the decoded length of the last block |
472 | 0 | let mut out = [0u8; 3]; |
473 | 0 | let last_block_len = if line.len() < base64_last_block_len { |
474 | 0 | let buffered_part_len = base64_last_block_len |
475 | 0 | .checked_sub(line.len()) |
476 | 0 | .ok_or(InvalidLength)?; |
477 | | |
478 | 0 | let offset = 4usize.checked_sub(buffered_part_len).ok_or(InvalidLength)?; |
479 | | |
480 | 0 | for i in 0..buffered_part_len { |
481 | 0 | buffer[i] = buffer[offset.checked_add(i).ok_or(InvalidLength)?]; |
482 | | } |
483 | | |
484 | 0 | buffer[buffered_part_len..][..line.len()].copy_from_slice(line.remaining); |
485 | 0 | let buffer_len = buffered_part_len |
486 | 0 | .checked_add(line.len()) |
487 | 0 | .ok_or(InvalidLength)?; |
488 | | |
489 | 0 | E::decode(&buffer[..buffer_len], &mut out)?.len() |
490 | | } else { |
491 | 0 | let last_block = line.slice_tail(base64_last_block_len)?; |
492 | 0 | E::decode(last_block, &mut out)?.len() |
493 | | }; |
494 | | |
495 | 0 | return decoded_len.checked_add(last_block_len).ok_or(InvalidLength); |
496 | | } |
497 | | } |
498 | | } |
499 | 0 | } |
500 | | } |
501 | | |
502 | | impl<'i> Iterator for LineReader<'i> { |
503 | | type Item = Result<Line<'i>, Error>; |
504 | | |
505 | 0 | fn next(&mut self) -> Option<Result<Line<'i>, Error>> { |
506 | 0 | if let Some(line_width) = self.line_width { |
507 | 0 | let rest = match self.remaining.get(line_width..) { |
508 | 0 | None | Some([]) => { |
509 | 0 | if self.remaining.is_empty() { |
510 | 0 | return None; |
511 | | } else { |
512 | 0 | let line = Line::new(self.remaining).trim_end(); |
513 | 0 | self.remaining = &[]; |
514 | 0 | return Some(Ok(line)); |
515 | | } |
516 | | } |
517 | 0 | Some([CHAR_CR, CHAR_LF, rest @ ..]) => rest, |
518 | 0 | Some([CHAR_CR, rest @ ..]) => rest, |
519 | 0 | Some([CHAR_LF, rest @ ..]) => rest, |
520 | | _ => { |
521 | | // Expected a leading newline |
522 | 0 | return Some(Err(Error::InvalidEncoding)); |
523 | | } |
524 | | }; |
525 | | |
526 | 0 | let line = Line::new(&self.remaining[..line_width]); |
527 | 0 | self.remaining = rest; |
528 | 0 | Some(Ok(line)) |
529 | 0 | } else if !self.remaining.is_empty() { |
530 | 0 | let line = Line::new(self.remaining).trim_end(); |
531 | 0 | self.remaining = b""; |
532 | | |
533 | 0 | if line.is_empty() { |
534 | 0 | None |
535 | | } else { |
536 | 0 | Some(Ok(line)) |
537 | | } |
538 | | } else { |
539 | 0 | None |
540 | | } |
541 | 0 | } |
542 | | } |
543 | | |
544 | | #[cfg(test)] |
545 | | #[allow(clippy::unwrap_used)] |
546 | | mod tests { |
547 | | use crate::{Base64, Base64Unpadded, Decoder, alphabet::Alphabet, test_vectors::*}; |
548 | | |
549 | | #[cfg(feature = "std")] |
550 | | use {alloc::vec::Vec, std::io::Read}; |
551 | | |
552 | | #[test] |
553 | | fn decode_padded() { |
554 | | decode_test(PADDED_BIN, || { |
555 | | Decoder::<Base64>::new(PADDED_BASE64.as_bytes()).unwrap() |
556 | | }) |
557 | | } |
558 | | |
559 | | #[test] |
560 | | fn decode_unpadded() { |
561 | | decode_test(UNPADDED_BIN, || { |
562 | | Decoder::<Base64Unpadded>::new(UNPADDED_BASE64.as_bytes()).unwrap() |
563 | | }) |
564 | | } |
565 | | |
566 | | #[test] |
567 | | fn decode_multiline_padded() { |
568 | | decode_test(MULTILINE_PADDED_BIN, || { |
569 | | Decoder::<Base64>::new_wrapped(MULTILINE_PADDED_BASE64.as_bytes(), 70).unwrap() |
570 | | }) |
571 | | } |
572 | | |
573 | | #[test] |
574 | | fn decode_multiline_unpadded() { |
575 | | decode_test(MULTILINE_UNPADDED_BIN, || { |
576 | | Decoder::<Base64Unpadded>::new_wrapped(MULTILINE_UNPADDED_BASE64.as_bytes(), 70) |
577 | | .unwrap() |
578 | | }) |
579 | | } |
580 | | |
581 | | #[cfg(feature = "std")] |
582 | | #[test] |
583 | | fn read_multiline_padded() { |
584 | | let mut decoder = |
585 | | Decoder::<Base64>::new_wrapped(MULTILINE_PADDED_BASE64.as_bytes(), 70).unwrap(); |
586 | | |
587 | | let mut buf = Vec::new(); |
588 | | let len = decoder.read_to_end(&mut buf).unwrap(); |
589 | | |
590 | | assert_eq!(len, MULTILINE_PADDED_BIN.len()); |
591 | | assert_eq!(buf.as_slice(), MULTILINE_PADDED_BIN); |
592 | | } |
593 | | |
594 | | #[cfg(feature = "std")] |
595 | | #[test] |
596 | | fn decode_empty_at_end() { |
597 | | let mut decoder = Decoder::<Base64>::new(b"AAAA").unwrap(); |
598 | | |
599 | | // Strip initial bytes |
600 | | let mut buf = vec![0u8; 3]; |
601 | | assert_eq!(decoder.decode(&mut buf), Ok(&vec![0u8; 3][..])); |
602 | | |
603 | | // Now try to read nothing from the end |
604 | | let mut buf: Vec<u8> = vec![]; |
605 | | |
606 | | assert_eq!(decoder.decode(&mut buf), Ok(&[][..])); |
607 | | } |
608 | | |
609 | | /// Core functionality of a decoding test |
610 | | #[allow(clippy::arithmetic_side_effects)] |
611 | | fn decode_test<'a, F, V>(expected: &[u8], f: F) |
612 | | where |
613 | | F: Fn() -> Decoder<'a, V>, |
614 | | V: Alphabet, |
615 | | { |
616 | | for chunk_size in 1..expected.len() { |
617 | | let mut decoder = f(); |
618 | | let mut remaining_len = decoder.remaining_len(); |
619 | | let mut buffer = [0u8; 1024]; |
620 | | |
621 | | for chunk in expected.chunks(chunk_size) { |
622 | | assert!(!decoder.is_finished()); |
623 | | let decoded = decoder.decode(&mut buffer[..chunk.len()]).unwrap(); |
624 | | assert_eq!(chunk, decoded); |
625 | | |
626 | | let dlen = decoded.len(); |
627 | | remaining_len -= dlen; |
628 | | assert_eq!(remaining_len, decoder.remaining_len()); |
629 | | } |
630 | | |
631 | | assert!(decoder.is_finished()); |
632 | | assert_eq!(decoder.remaining_len(), 0); |
633 | | } |
634 | | } |
635 | | } |