Coverage Report

Created: 2026-08-13 06:34

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