Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  tests.rs

  Sprache: Rust
 

// rstest_reuse template functions have unused variables
#![allow(unused_variables)]

use rand::{
    self,
    distributions::{self, Distribution as _},
    rngs, Rng as _, SeedableRng as _,
};
use rstest::rstest;
use rstest_reuse::{apply, template};
use std::{collections, fmt, io::Read as _};

use crate::{
    alphabet::{Alphabet, STANDARD},
    encode::add_padding,
    encoded_len,
    engine::{
        general_purpose, naive, Config, DecodeEstimate, DecodeMetadata, DecodePaddingMode, Engine,
    },
    read::DecoderReader,
    tests::{assert_encode_sanity, random_alphabet, random_config},
    DecodeError, DecodeSliceError, PAD_BYTE,
};

// the case::foo syntax includes the "foo" in the generated test method names
#[template]
#[rstest(engine_wrapper,
case::general_purpose(GeneralPurposeWrapper {}),
case::naive(NaiveWrapper {}),
case::decoder_reader(DecoderReaderEngineWrapper {}),
)]
fn all_engines<E: EngineWrapper>(engine_wrapper: E) {}

/// Some decode tests don't make sense for use with `DecoderReader` as they are difficult to
/// reason about or otherwise inapplicable given how DecoderReader slice up its input along
/// chunk boundaries.
#[template]
#[rstest(engine_wrapper,
case::general_purpose(GeneralPurposeWrapper {}),
case::naive(NaiveWrapper {}),
)]
fn all_engines_except_decoder_reader<E: EngineWrapper>(engine_wrapper: E) {}

#[apply(all_engines)]
fn rfc_test_vectors_std_alphabet<E: EngineWrapper>(engine_wrapper: E) {
    let data = vec![
        (""""),
        ("f""Zg=="),
        ("fo""Zm8="),
        ("foo""Zm9v"),
        ("foob""Zm9vYg=="),
        ("fooba""Zm9vYmE="),
        ("foobar""Zm9vYmFy"),
    ];

    let engine = E::standard();
    let engine_no_padding = E::standard_unpadded();

    for (orig, encoded) in &data {
        let encoded_without_padding = encoded.trim_end_matches('=');

        // unpadded
        {
            let mut encode_buf = [0_u8; 8];
            let mut decode_buf = [0_u8; 6];

            let encode_len =
                engine_no_padding.internal_encode(orig.as_bytes(), &mut encode_buf[..]);
            assert_eq!(
                &encoded_without_padding,
                &std::str::from_utf8(&encode_buf[0..encode_len]).unwrap()
            );
            let decode_len = engine_no_padding
                .decode_slice_unchecked(encoded_without_padding.as_bytes(), &mut decode_buf[..])
                .unwrap();
            assert_eq!(orig.len(), decode_len);

            assert_eq!(
                orig,
                &std::str::from_utf8(&decode_buf[0..decode_len]).unwrap()
            );

            // if there was any padding originally, the no padding engine won't decode it
            if encoded.as_bytes().contains(&PAD_BYTE) {
                assert_eq!(
                    Err(DecodeError::InvalidPadding),
                    engine_no_padding.decode(encoded)
                )
            }
        }

        // padded
        {
            let mut encode_buf = [0_u8; 8];
            let mut decode_buf = [0_u8; 6];

            let encode_len = engine.internal_encode(orig.as_bytes(), &mut encode_buf[..]);
            assert_eq!(
                // doesn't have padding added yet
                &encoded_without_padding,
                &std::str::from_utf8(&encode_buf[0..encode_len]).unwrap()
            );
            let pad_len = add_padding(encode_len, &mut encode_buf[encode_len..]);
            assert_eq!(encoded.as_bytes(), &encode_buf[..encode_len + pad_len]);

            let decode_len = engine
                .decode_slice_unchecked(encoded.as_bytes(), &mut decode_buf[..])
                .unwrap();
            assert_eq!(orig.len(), decode_len);

            assert_eq!(
                orig,
                &std::str::from_utf8(&decode_buf[0..decode_len]).unwrap()
            );

            // if there was (canonical) padding, and we remove it, the standard engine won't decode
            if encoded.as_bytes().contains(&PAD_BYTE) {
                assert_eq!(
                    Err(DecodeError::InvalidPadding),
                    engine.decode(encoded_without_padding)
                )
            }
        }
    }
}

#[apply(all_engines)]
fn roundtrip_random<E: EngineWrapper>(engine_wrapper: E) {
    let mut rng = seeded_rng();

    let mut orig_data = Vec::<u8>::new();
    let mut encode_buf = Vec::<u8>::new();
    let mut decode_buf = Vec::<u8>::new();

    let len_range = distributions::Uniform::new(11_000);

    for _ in 0..10_000 {
        let engine = E::random(&mut rng);

        orig_data.clear();
        encode_buf.clear();
        decode_buf.clear();

        let (orig_len, _, encoded_len) = generate_random_encoded_data(
            &engine,
            &mut orig_data,
            &mut encode_buf,
            &mut rng,
            &len_range,
        );

        // exactly the right size
        decode_buf.resize(orig_len, 0);

        let dec_len = engine
            .decode_slice_unchecked(&encode_buf[0..encoded_len], &mut decode_buf[..])
            .unwrap();

        assert_eq!(orig_len, dec_len);
        assert_eq!(&orig_data[..], &decode_buf[..dec_len]);
    }
}

#[apply(all_engines)]
fn encode_doesnt_write_extra_bytes<E: EngineWrapper>(engine_wrapper: E) {
    let mut rng = seeded_rng();

    let mut orig_data = Vec::<u8>::new();
    let mut encode_buf = Vec::<u8>::new();
    let mut encode_buf_backup = Vec::<u8>::new();

    let input_len_range = distributions::Uniform::new(01000);

    for _ in 0..10_000 {
        let engine = E::random(&mut rng);
        let padded = engine.config().encode_padding();

        orig_data.clear();
        encode_buf.clear();
        encode_buf_backup.clear();

        let orig_len = fill_rand(&mut orig_data, &>mut rng, &input_len_range);

        let prefix_len = 1024;
        // plenty of prefix and suffix
        fill_rand_len(&mut encode_buf, &mut rng, prefix_len * 2 + orig_len * 2);
        encode_buf_backup.extend_from_slice(&encode_buf[..]);

        let expected_encode_len_no_pad = encoded_len(orig_len, false).unwrap();

        let encoded_len_no_pad =
            engine.internal_encode(&orig_data[..], &mut encode_buf[prefix_len..]);
        assert_eq!(expected_encode_len_no_pad, encoded_len_no_pad);

        // no writes past what it claimed to write
        assert_eq!(&encode_buf_backup[..prefix_len], &encode_buf[..prefix_len]);
        assert_eq!(
            &encode_buf_backup[(prefix_len + encoded_len_no_pad)..],
            &encode_buf[(prefix_len + encoded_len_no_pad)..]
        );

        let encoded_data = &encode_buf[prefix_len..(prefix_len + encoded_len_no_pad)];
        assert_encode_sanity(
            std::str::from_utf8(encoded_data).unwrap(),
            // engines don't pad
            false,
            orig_len,
        );

        // pad so we can decode it in case our random engine requires padding
        let pad_len = if padded {
            add_padding(
                encoded_len_no_pad,
                &mut encode_buf[prefix_len + encoded_len_no_pad..],
            )
        } else {
            0
        };

        assert_eq!(
            orig_data,
            engine
                .decode(&encode_buf[prefix_len..(prefix_len + encoded_len_no_pad + pad_len)],)
                .unwrap()
        );
    }
}

#[apply(all_engines)]
fn encode_engine_slice_fits_into_precisely_sized_slice<E: EngineWrapper>(engine_wrapper: E) {
    let mut orig_data = Vec::new();
    let mut encoded_data = Vec::new();
    let mut decoded = Vec::new();

    let input_len_range = distributions::Uniform::new(01000);

    let mut rng = rngs::SmallRng::from_entropy();

    for _ in 0..10_000 {
        orig_data.clear();
        encoded_data.clear();
        decoded.clear();

        let input_len = input_len_range.sample(&mut rng);

        for _ in 0..input_len {
            orig_data.push(rng.gen());
        }

        let engine = E::random(&mut rng);

        let encoded_size = encoded_len(input_len, engine.config().encode_padding()).unwrap();

        encoded_data.resize(encoded_size, 0);

        assert_eq!(
            encoded_size,
            engine.encode_slice(&orig_data, &mut encoded_data).unwrap()
        );

        assert_encode_sanity(
            std::str::from_utf8(&encoded_data[0..encoded_size]).unwrap(),
            engine.config().encode_padding(),
            input_len,
        );

        engine
            .decode_vec(&encoded_data[0..encoded_size], &mut decoded)
            .unwrap();
        assert_eq!(orig_data, decoded);
    }
}

#[apply(all_engines)]
fn decode_doesnt_write_extra_bytes<E>(engine_wrapper: E)
where
    E: EngineWrapper,
    <<E as EngineWrapper>::Engine as Engine>::Config: fmt::Debug,
{
    let mut rng = seeded_rng();

    let mut orig_data = Vec::<u8>::new();
    let mut encode_buf = Vec::<u8>::new();
    let mut decode_buf = Vec::<u8>::new();
    let mut decode_buf_backup = Vec::<u8>::new();

    let len_range = distributions::Uniform::new(11_000);

    for _ in 0..10_000 {
        let engine = E::random(&mut rng);

        orig_data.clear();
        encode_buf.clear();
        decode_buf.clear();
        decode_buf_backup.clear();

        let orig_len = fill_rand(&mut orig_data, &>mut rng, &len_range);
        encode_buf.resize(orig_len * 2 + 1000);

        let encoded_len = engine
            .encode_slice(&orig_data[..], &mut encode_buf[..])
            .unwrap();
        encode_buf.truncate(encoded_len);

        // oversize decode buffer so we can easily tell if it writes anything more than
        // just the decoded data
        let prefix_len = 1024;
        // plenty of prefix and suffix
        fill_rand_len(&mut decode_buf, &mut rng, prefix_len * 2 + orig_len * 2);
        decode_buf_backup.extend_from_slice(&decode_buf[..]);

        let dec_len = engine
            .decode_slice_unchecked(&encode_buf, &mut decode_buf[prefix_len..])
            .unwrap();

        assert_eq!(orig_len, dec_len);
        assert_eq!(
            &orig_data[..],
            &decode_buf[prefix_len..prefix_len + dec_len]
        );
        assert_eq!(&decode_buf_backup[..prefix_len], &decode_buf[..prefix_len]);
        assert_eq!(
            &decode_buf_backup[prefix_len + dec_len..],
            &decode_buf[prefix_len + dec_len..]
        );
    }
}

#[apply(all_engines)]
fn decode_detect_invalid_last_symbol<E: EngineWrapper>(engine_wrapper: E) {
    // 0xFF -> "/w==", so all letters > w, 0-9, and '+', '/' should get InvalidLastSymbol
    let engine = E::standard();

    assert_eq!(Ok(vec![0x89, 0x85]), engine.decode("iYU="));
    assert_eq!(Ok(vec![0xFF]), engine.decode("/w=="));

    for (suffix, offset) in vec![
        // suffix, offset of bad byte from start of suffix
        ("/x=="1_usize),
        ("/z=="1_usize),
        ("/0=="1_usize),
        ("/9=="1_usize),
        ("/+=="1_usize),
        ("//==", 1_usize),
        // trailing 01
        ("iYV="2_usize),
        // trailing 10
        ("iYW="2_usize),
        // trailing 11
        ("iYX="2_usize),
    ] {
        for prefix_quads in 0..256 {
            let mut encoded = "AAAA".repeat(prefix_quads);
            encoded.push_str(suffix);

            assert_eq!(
                Err(DecodeError::InvalidLastSymbol(
                    encoded.len() - 4 + offset,
                    suffix.as_bytes()[offset],
                )),
                engine.decode(encoded.as_str())
            );
        }
    }
}

#[apply(all_engines)]
fn decode_detect_1_valid_symbol_in_last_quad_invalid_length<E: EngineWrapper>(engine_wrapper: E) {
    for len in (0_usize..256).map(|len| len * 4 + 1) {
        for mode in all_pad_modes() {
            let mut input = vec![b'A'; len];

            let engine = E::standard_with_pad_mode(true, mode);

            assert_eq!(Err(DecodeError::InvalidLength(len)), engine.decode(&input));
            // if we add padding, then the first pad byte in the quad is invalid because it should
            // be the second symbol
            for _ in 0..3 {
                input.push(PAD_BYTE);
                assert_eq!(
                    Err(DecodeError::InvalidByte(len, PAD_BYTE)),
                    engine.decode(&input)
                );
            }
        }
    }
}

#[apply(all_engines)]
fn decode_detect_1_invalid_byte_in_last_quad_invalid_byte<E: EngineWrapper>(engine_wrapper: E) {
    for prefix_len in (0_usize..256).map(|len| len * 4) {
        for mode in all_pad_modes() {
            let mut input = vec![b'A'; prefix_len];
            input.push(b'*');

            let engine = E::standard_with_pad_mode(true, mode);

            assert_eq!(
                Err(DecodeError::InvalidByte(prefix_len, b'*')),
                engine.decode(&input)
            );
            // adding padding doesn't matter
            for _ in 0..3 {
                input.push(PAD_BYTE);
                assert_eq!(
                    Err(DecodeError::InvalidByte(prefix_len, b'*')),
                    engine.decode(&input)
                );
            }
        }
    }
}

#[apply(all_engines)]
fn decode_detect_invalid_last_symbol_every_possible_two_symbols<E: EngineWrapper>(
    engine_wrapper: E,
) {
    let engine = E::standard();

    let mut base64_to_bytes = collections::HashMap::new();

    for b in 0_u8..=255 {
        let mut b64 = vec![0_u8; 4];
        assert_eq!(2, engine.internal_encode(&[b], &mut b64[..]));
        let _ = add_padding(2, &mut b64[2..]);

        assert!(base64_to_bytes.insert(b64, vec![b]).is_none());
    }

    // every possible combination of trailing symbols must either decode to 1 byte or get InvalidLastSymbol, with or without any leading chunks

    let mut prefix = Vec::new();
    for _ in 0..256 {
        let mut clone = prefix.clone();

        let mut symbols = [0_u8; 4];
        for &s1 in STANDARD.symbols.iter() {
            symbols[0] = s1;
            for &s2 in STANDARD.symbols.iter() {
                symbols[1] = s2;
                symbols[2] = PAD_BYTE;
                symbols[3] = PAD_BYTE;

                // chop off previous symbols
                clone.truncate(prefix.len());
                clone.extend_from_slice(&symbols[..]);
                let decoded_prefix_len = prefix.len() / 4 * 3;

                match base64_to_bytes.get(&symbols[..]) {
                    Some(bytes) => {
                        let res = engine
                            .decode(&clone)
                            // remove prefix
                            .map(|decoded| decoded[decoded_prefix_len..].to_vec());

                        assert_eq!(Ok(bytes.clone()), res);
                    }
                    None => assert_eq!(
                        Err(DecodeError::InvalidLastSymbol(1, s2)),
                        engine.decode(&symbols[..])
                    ),
                }
            }
        }

        prefix.extend_from_slice(b"AAAA");
    }
}

#[apply(all_engines)]
fn decode_detect_invalid_last_symbol_every_possible_three_symbols<E: EngineWrapper>(
    engine_wrapper: E,
) {
    let engine = E::standard();

    let mut base64_to_bytes = collections::HashMap::new();

    let mut bytes = [0_u8; 2];
    for b1 in 0_u8..=255 {
        bytes[0] = b1;
        for b2 in 0_u8..=255 {
            bytes[1] = b2;
            let mut b64 = vec![0_u8; 4];
            assert_eq!(3, engine.internal_encode(&bytes, &mut b64[..]));
            let _ = add_padding(3, &mut b64[3..]);

            let mut v = Vec::with_capacity(2);
            v.extend_from_slice(&bytes[..]);

            assert!(base64_to_bytes.insert(b64, v).is_none());
        }
    }

    // every possible combination of symbols must either decode to 2 bytes or get InvalidLastSymbol, with or without any leading chunks

    let mut prefix = Vec::new();
    let mut input = Vec::new();
    for _ in 0..256 {
        input.clear();
        input.extend_from_slice(&prefix);

        let mut symbols = [0_u8; 4];
        for &s1 in STANDARD.symbols.iter() {
            symbols[0] = s1;
            for &s2 in STANDARD.symbols.iter() {
                symbols[1] = s2;
                for &s3 in STANDARD.symbols.iter() {
                    symbols[2] = s3;
                    symbols[3] = PAD_BYTE;

                    // chop off previous symbols
                    input.truncate(prefix.len());
                    input.extend_from_slice(&symbols[..]);
                    let decoded_prefix_len = prefix.len() / 4 * 3;

                    match base64_to_bytes.get(&symbols[..]) {
                        Some(bytes) => {
                            let res = engine
                                .decode(&input)
                                // remove prefix
                                .map(|decoded| decoded[decoded_prefix_len..].to_vec());

                            assert_eq!(Ok(bytes.clone()), res);
                        }
                        None => assert_eq!(
                            Err(DecodeError::InvalidLastSymbol(2, s3)),
                            engine.decode(&symbols[..])
                        ),
                    }
                }
            }
        }
        prefix.extend_from_slice(b"AAAA");
    }
}

#[apply(all_engines)]
fn decode_invalid_trailing_bits_ignored_when_configured<E: EngineWrapper>(engine_wrapper: E) {
    let strict = E::standard();
    let forgiving = E::standard_allow_trailing_bits();

    fn assert_tolerant_decode<E: Engine>(
        engine: &E,
        input: &mut String,
        b64_prefix_len: usize,
        expected_decode_bytes: Vec<u8>,
        data: &str,
    ) {
        let prefixed = prefixed_data(input, b64_prefix_len, data);
        let decoded = engine.decode(prefixed);
        // prefix is always complete chunks
        let decoded_prefix_len = b64_prefix_len / 4 * 3;
        assert_eq!(
            Ok(expected_decode_bytes),
            decoded.map(|v| v[decoded_prefix_len..].to_vec())
        );
    }

    let mut prefix = String::new();
    for _ in 0..256 {
        let mut input = prefix.clone();

        // example from https://github.com/marshallpierce/rust-base64/issues/75
        assert!(strict
            .decode(prefixed_data(&mut input, prefix.len(), "/w=="))
            .is_ok());
        assert!(strict
            .decode(prefixed_data(&mut input, prefix.len(), "iYU="))
            .is_ok());
        // trailing 01
        assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![255], "/x==");
        assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![137133], "iYV=");
        // trailing 10
        assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![255], "/y==");
        assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![137133], "iYW=");
        // trailing 11
        assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![255], "/z==");
        assert_tolerant_decode(&forgiving, &mut input, prefix.len(), vec![137133], "iYX=");

        prefix.push_str("AAAA");
    }
}

#[apply(all_engines)]
fn decode_invalid_byte_error<E: EngineWrapper>(engine_wrapper: E) {
    let mut rng = seeded_rng();

    let mut orig_data = Vec::<u8>::new();
    let mut encode_buf = Vec::<u8>::new();
    let mut decode_buf = Vec::<u8>::new();

    let len_range = distributions::Uniform::new(11_000);

    for _ in 0..100_000 {
        let alphabet = random_alphabet(&mut rng);
        let engine = E::random_alphabet(&mut rng, alphabet);

        orig_data.clear();
        encode_buf.clear();
        decode_buf.clear();

        let (orig_len, encoded_len_just_data, encoded_len_with_padding) =
            generate_random_encoded_data(
                &engine,
                &mut orig_data,
                &mut encode_buf,
                &mut rng,
                &len_range,
            );

        // exactly the right size
        decode_buf.resize(orig_len, 0);

        // replace one encoded byte with an invalid byte
        let invalid_byte: u8 = loop {
            let byte: u8 = rng.gen();

            if alphabet.symbols.contains(&byte) || byte == PAD_BYTE {
                continue;
            } else {
                break byte;
            }
        };

        let invalid_range = distributions::Uniform::new(0, orig_len);
        let invalid_index = invalid_range.sample(&mut rng);
        encode_buf[invalid_index] = invalid_byte;

        assert_eq!(
            Err(DecodeError::InvalidByte(invalid_index, invalid_byte)),
            engine.decode_slice_unchecked(
                &encode_buf[0..encoded_len_with_padding],
                &mut decode_buf[..],
            )
        );
    }
}

/// Any amount of padding anywhere before the final non padding character = invalid byte at first
/// pad byte.
/// From this and [decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad_non_canonical_padding_suffix_all_modes],
/// we know padding must extend contiguously to the end of the input.
#[apply(all_engines)]
fn decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad_all_modes<
    E: EngineWrapper,
>(
    engine_wrapper: E,
) {
    // Different amounts of padding, w/ offset from end for the last non-padding char.
    // Only canonical padding, so Canonical mode will work.
    let suffixes = &[("AA=="2), ("AAA="1), ("AAAA"0)];

    for mode in pad_modes_allowing_padding() {
        // We don't encode, so we don't care about encode padding.
        let engine = E::standard_with_pad_mode(true, mode);

        decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad(
            engine,
            suffixes.as_slice(),
        );
    }
}

/// See [decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad_all_modes]
#[apply(all_engines)]
fn decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad_non_canonical_padding_suffix<
    E: EngineWrapper,
>(
    engine_wrapper: E,
) {
    // Different amounts of padding, w/ offset from end for the last non-padding char, and
    // non-canonical padding.
    let suffixes = [
        ("AA=="2),
        ("AA="1),
        ("AA"0),
        ("AAA="1),
        ("AAA"0),
        ("AAAA"0),
    ];

    // We don't encode, so we don't care about encode padding.
    // Decoding is indifferent so that we don't get caught by missing padding on the last quad
    let engine = E::standard_with_pad_mode(true, DecodePaddingMode::Indifferent);

    decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad(
        engine,
        suffixes.as_slice(),
    )
}

fn decode_padding_before_final_non_padding_char_error_invalid_byte_at_first_pad(
    engine: impl Engine,
    suffixes: &[(&str, usize)],
) {
    let mut rng = seeded_rng();

    let prefix_quads_range = distributions::Uniform::from(0..=256);

    for _ in 0..100_000 {
        for (suffix, suffix_offset) in suffixes.iter() {
            let mut s = "AAAA".repeat(prefix_quads_range.sample(&mut rng));
            s.push_str(suffix);
            let mut encoded = s.into_bytes();

            // calculate a range to write padding into that leaves at least one non padding char
            let last_non_padding_offset = encoded.len() - 1 - suffix_offset;

            // don't include last non padding char as it must stay not padding
            let padding_end = rng.gen_range(0..last_non_padding_offset);

            // don't use more than 100 bytes of padding, but also use shorter lengths when
            // padding_end is near the start of the encoded data to avoid biasing to padding
            // the entire prefix on short lengths
            let padding_len = rng.gen_range(1..=usize::min(100, padding_end + 1));
            let padding_start = padding_end.saturating_sub(padding_len);

            encoded[padding_start..=padding_end].fill(PAD_BYTE);

            // should still have non-padding before any final padding
            assert_ne!(PAD_BYTE, encoded[last_non_padding_offset]);
            assert_eq!(
                Err(DecodeError::InvalidByte(padding_start, PAD_BYTE)),
                engine.decode(&encoded),
                "len: {}, input: {}",
                encoded.len(),
                String::from_utf8(encoded).unwrap()
            );
        }
    }
}

/// Any amount of padding before final chunk that crosses over into final chunk with 1-4 bytes =
/// invalid byte at first pad byte.
/// From this we know the padding must start in the final chunk.
#[apply(all_engines)]
fn decode_padding_starts_before_final_chunk_error_invalid_byte_at_first_pad<E: EngineWrapper>(
    engine_wrapper: E,
) {
    let mut rng = seeded_rng();

    // must have at least one prefix quad
    let prefix_quads_range = distributions::Uniform::from(1..256);
    let suffix_pad_len_range = distributions::Uniform::from(1..=4);
    // don't use no-padding mode, as the reader decode might decode a block that ends with
    // valid padding, which should then be referenced when encountering the later invalid byte
    for mode in pad_modes_allowing_padding() {
        // we don't encode so we don't care about encode padding
        let engine = E::standard_with_pad_mode(true, mode);
        for _ in 0..100_000 {
            let suffix_len = suffix_pad_len_range.sample(&mut rng);
            // all 0 bits so we don't hit InvalidLastSymbol with the reader decoder
            let mut encoded = "AAAA"
                .repeat(prefix_quads_range.sample(&mut rng))
                .into_bytes();
            encoded.resize(encoded.len() + suffix_len, PAD_BYTE);

            // amount of padding must be long enough to extend back from suffix into previous
            // quads
            let padding_len = rng.gen_range(suffix_len + 1..encoded.len());
            // no non-padding after padding in this test, so padding goes to the end
            let padding_start = encoded.len() - padding_len;
            encoded[padding_start..].fill(PAD_BYTE);

            assert_eq!(
                Err(DecodeError::InvalidByte(padding_start, PAD_BYTE)),
                engine.decode(&encoded),
                "suffix_len: {}, padding_len: {}, b64: {}",
                suffix_len,
                padding_len,
                std::str::from_utf8(&encoded).unwrap()
            );
        }
    }
}

/// 0-1 bytes of data before any amount of padding in final chunk = invalid byte, since padding
/// is not valid data (consistent with error for pad bytes in earlier chunks).
/// From this we know there must be 2-3 bytes of data before padding
#[apply(all_engines)]
fn decode_too_little_data_before_padding_error_invalid_byte<E: EngineWrapper>(engine_wrapper: E) {
    let mut rng = seeded_rng();

    // want to test no prefix quad case, so start at 0
    let prefix_quads_range = distributions::Uniform::from(0_usize..256);
    let suffix_data_len_range = distributions::Uniform::from(0_usize..=1);
    for mode in all_pad_modes() {
        // we don't encode so we don't care about encode padding
        let engine = E::standard_with_pad_mode(true, mode);
        for _ in 0..100_000 {
            let suffix_data_len = suffix_data_len_range.sample(&mut rng);
            let prefix_quad_len = prefix_quads_range.sample(&mut rng);

            // for all possible padding lengths
            for padding_len in 1..=(4 - suffix_data_len) {
                let mut encoded = "ABCD".repeat(prefix_quad_len).into_bytes();
                encoded.resize(encoded.len() + suffix_data_len, b'A');
                encoded.resize(encoded.len() + padding_len, PAD_BYTE);

                assert_eq!(
                    Err(DecodeError::InvalidByte(
                        prefix_quad_len * 4 + suffix_data_len,
                        PAD_BYTE,
                    )),
                    engine.decode(&encoded),
                    "input {} suffix data len {} pad len {}",
                    String::from_utf8(encoded).unwrap(),
                    suffix_data_len,
                    padding_len
                );
            }
        }
    }
}

// https://eprint.iacr.org/2022/361.pdf table 2, test 1
#[apply(all_engines)]
fn decode_malleability_test_case_3_byte_suffix_valid<E: EngineWrapper>(engine_wrapper: E) {
    assert_eq!(
        b"Hello".as_slice(),
        &E::standard().decode("SGVsbG8=").unwrap()
    );
}

// https://eprint.iacr.org/2022/361.pdf table 2, test 2
#[apply(all_engines)]
fn decode_malleability_test_case_3_byte_suffix_invalid_trailing_symbol<E: EngineWrapper>(
    engine_wrapper: E,
) {
    assert_eq!(
        DecodeError::InvalidLastSymbol(60x39),
        E::standard().decode("SGVsbG9=").unwrap_err()
    );
}

// https://eprint.iacr.org/2022/361.pdf table 2, test 3
#[apply(all_engines)]
fn decode_malleability_test_case_3_byte_suffix_no_padding<E: EngineWrapper>(engine_wrapper: E) {
    assert_eq!(
        DecodeError::InvalidPadding,
        E::standard().decode("SGVsbG9").unwrap_err()
    );
}

// https://eprint.iacr.org/2022/361.pdf table 2, test 4
#[apply(all_engines)]
fn decode_malleability_test_case_2_byte_suffix_valid_two_padding_symbols<E: EngineWrapper>(
    engine_wrapper: E,
) {
    assert_eq!(
        b"Hell".as_slice(),
        &E::standard().decode("SGVsbA==").unwrap()
    );
}

// https://eprint.iacr.org/2022/361.pdf table 2, test 5
#[apply(all_engines)]
fn decode_malleability_test_case_2_byte_suffix_short_padding<E: EngineWrapper>(engine_wrapper: E) {
    assert_eq!(
        DecodeError::InvalidPadding,
        E::standard().decode("SGVsbA=").unwrap_err()
    );
}

// https://eprint.iacr.org/2022/361.pdf table 2, test 6
#[apply(all_engines)]
fn decode_malleability_test_case_2_byte_suffix_no_padding<E: EngineWrapper>(engine_wrapper: E) {
    assert_eq!(
        DecodeError::InvalidPadding,
        E::standard().decode("SGVsbA").unwrap_err()
    );
}

// https://eprint.iacr.org/2022/361.pdf table 2, test 7
// DecoderReader pseudo-engine gets InvalidByte at 8 (extra padding) since it decodes the first
// two complete quads correctly.
#[apply(all_engines_except_decoder_reader)]
fn decode_malleability_test_case_2_byte_suffix_too_much_padding<E: EngineWrapper>(
    engine_wrapper: E,
) {
    assert_eq!(
        DecodeError::InvalidByte(6, PAD_BYTE),
        E::standard().decode("SGVsbA====").unwrap_err()
    );
}

/// Requires canonical padding -> accepts 2 + 2, 3 + 1, 4 + 0 final quad configurations
#[apply(all_engines)]
fn decode_pad_mode_requires_canonical_accepts_canonical<E: EngineWrapper>(engine_wrapper: E) {
    assert_all_suffixes_ok(
        E::standard_with_pad_mode(true, DecodePaddingMode::RequireCanonical),
        vec!["/w==""iYU=""AAAA"],
    );
}

/// Requires canonical padding -> rejects 2 + 0-1, 3 + 0 final chunk configurations
#[apply(all_engines)]
fn decode_pad_mode_requires_canonical_rejects_non_canonical<E: EngineWrapper>(engine_wrapper: E) {
    let engine = E::standard_with_pad_mode(true, DecodePaddingMode::RequireCanonical);

    let suffixes = ["/w""/w=""iYU"];
    for num_prefix_quads in 0..256 {
        for &suffix in suffixes.iter() {
            let mut encoded = "AAAA".repeat(num_prefix_quads);
            encoded.push_str(suffix);

            let res = engine.decode(&encoded);

            assert_eq!(Err(DecodeError::InvalidPadding), res);
        }
    }
}

/// Requires no padding -> accepts 2 + 0, 3 + 0, 4 + 0 final chunk configuration
#[apply(all_engines)]
fn decode_pad_mode_requires_no_padding_accepts_no_padding<E: EngineWrapper>(engine_wrapper: E) {
    assert_all_suffixes_ok(
        E::standard_with_pad_mode(true, DecodePaddingMode::RequireNone),
        vec!["/w""iYU""AAAA"],
    );
}

/// Requires no padding -> rejects 2 + 1-2, 3 + 1 final chunk configuration
#[apply(all_engines)]
fn decode_pad_mode_requires_no_padding_rejects_any_padding<E: EngineWrapper>(engine_wrapper: E) {
    let engine = E::standard_with_pad_mode(true, DecodePaddingMode::RequireNone);

    let suffixes = ["/w=""/w==""iYU="];
    for num_prefix_quads in 0..256 {
        for &suffix in suffixes.iter() {
            let mut encoded = "AAAA".repeat(num_prefix_quads);
            encoded.push_str(suffix);

            let res = engine.decode(&encoded);

            assert_eq!(Err(DecodeError::InvalidPadding), res);
        }
    }
}

/// Indifferent padding accepts 2 + 0-2, 3 + 0-1, 4 + 0 final chunk configuration
#[apply(all_engines)]
fn decode_pad_mode_indifferent_padding_accepts_anything<E: EngineWrapper>(engine_wrapper: E) {
    assert_all_suffixes_ok(
        E::standard_with_pad_mode(true, DecodePaddingMode::Indifferent),
        vec!["/w""/w=""/w==""iYU""iYU=""AAAA"],
    );
}

/// 1 trailing byte that's not padding is detected as invalid byte even though there's padding
/// in the middle of the input. This is essentially mandating the eager check for 1 trailing byte
/// to catch the \n suffix case.
// DecoderReader pseudo-engine can't handle DecodePaddingMode::RequireNone since it will decode
// a complete quad with padding in it before encountering the stray byte that makes it an invalid
// length
#[apply(all_engines_except_decoder_reader)]
fn decode_invalid_trailing_bytes_all_pad_modes_invalid_byte<E: EngineWrapper>(engine_wrapper: E) {
    for mode in all_pad_modes() {
        do_invalid_trailing_byte(E::standard_with_pad_mode(true, mode), mode);
    }
}

#[apply(all_engines)]
fn decode_invalid_trailing_bytes_invalid_byte<E: EngineWrapper>(engine_wrapper: E) {
    // excluding no padding mode because the DecoderWrapper pseudo-engine will fail with
    // InvalidPadding because it will decode the last complete quad with padding first
    for mode in pad_modes_allowing_padding() {
        do_invalid_trailing_byte(E::standard_with_pad_mode(true, mode), mode);
    }
}
fn do_invalid_trailing_byte(engine: impl Engine, mode: DecodePaddingMode) {
    for last_byte in [b'*', b'\n'] {
        for num_prefix_quads in 0..256 {
            let mut s: String = "ABCD".repeat(num_prefix_quads);
            s.push_str("Cg==");
            let mut input = s.into_bytes();
            input.push(last_byte);

            // The case of trailing newlines is common enough to warrant a test for a good error
            // message.
            assert_eq!(
                Err(DecodeError::InvalidByte(
                    num_prefix_quads * 4 + 4,
                    last_byte
                )),
                engine.decode(&input),
                "mode: {:?}, input: {}",
                mode,
                String::from_utf8(input).unwrap()
            );
        }
    }
}

/// When there's 1 trailing byte, but it's padding, it's only InvalidByte if there isn't padding
/// earlier.
#[apply(all_engines)]
fn decode_invalid_trailing_padding_as_invalid_byte_at_first_pad_byte<E: EngineWrapper>(
    engine_wrapper: E,
) {
    // excluding no padding mode because the DecoderWrapper pseudo-engine will fail with
    // InvalidPadding because it will decode the last complete quad with padding first
    for mode in pad_modes_allowing_padding() {
        do_invalid_trailing_padding_as_invalid_byte_at_first_padding(
            E::standard_with_pad_mode(true, mode),
            mode,
        );
    }
}

// DecoderReader pseudo-engine can't handle DecodePaddingMode::RequireNone since it will decode
// a complete quad with padding in it before encountering the stray byte that makes it an invalid
// length
#[apply(all_engines_except_decoder_reader)]
fn decode_invalid_trailing_padding_as_invalid_byte_at_first_byte_all_modes<E: EngineWrapper>(
    engine_wrapper: E,
) {
    for mode in all_pad_modes() {
        do_invalid_trailing_padding_as_invalid_byte_at_first_padding(
            E::standard_with_pad_mode(true, mode),
            mode,
        );
    }
}
fn do_invalid_trailing_padding_as_invalid_byte_at_first_padding(
    engine: impl Engine,
    mode: DecodePaddingMode,
) {
    for num_prefix_quads in 0..256 {
        for (suffix, pad_offset) in [("AA==="2), ("AAA=="3), ("AAAA="4)] {
            let mut s: String = "ABCD".repeat(num_prefix_quads);
            s.push_str(suffix);

            assert_eq!(
                // pad after `g`, not the last one
                Err(DecodeError::InvalidByte(
                    num_prefix_quads * 4 + pad_offset,
                    PAD_BYTE
                )),
                engine.decode(&s),
                "mode: {:?}, input: {}",
                mode,
                s
            );
        }
    }
}

#[apply(all_engines)]
fn decode_into_slice_fits_in_precisely_sized_slice<E: EngineWrapper>(engine_wrapper: E) {
    let mut orig_data = Vec::new();
    let mut encoded_data = String::new();
    let mut decode_buf = Vec::new();

    let input_len_range = distributions::Uniform::new(01000);
    let mut rng = rngs::SmallRng::from_entropy();

    for _ in 0..10_000 {
        orig_data.clear();
        encoded_data.clear();
        decode_buf.clear();

        let input_len = input_len_range.sample(&mut rng);

        for _ in 0..input_len {
            orig_data.push(rng.gen());
        }

        let engine = E::random(&mut rng);
        engine.encode_string(&orig_data, &mut encoded_data);
        assert_encode_sanity(&encoded_data, engine.config().encode_padding(), input_len);

        decode_buf.resize(input_len, 0);
        // decode into the non-empty buf
        let decode_bytes_written = engine
            .decode_slice_unchecked(encoded_data.as_bytes(), &mut decode_buf[..])
            .unwrap();
        assert_eq!(orig_data.len(), decode_bytes_written);
        assert_eq!(orig_data, decode_buf);

        // same for checked variant
        decode_buf.clear();
        decode_buf.resize(input_len, 0);
        // decode into the non-empty buf
        let decode_bytes_written = engine
            .decode_slice(encoded_data.as_bytes(), &mut decode_buf[..])
            .unwrap();
        assert_eq!(orig_data.len(), decode_bytes_written);
        assert_eq!(orig_data, decode_buf);
    }
}

#[apply(all_engines)]
fn inner_decode_reports_padding_position<E: EngineWrapper>(engine_wrapper: E) {
    let mut b64 = String::new();
    let mut decoded = Vec::new();
    let engine = E::standard();

    for pad_position in 1..10_000 {
        b64.clear();
        decoded.clear();
        // plenty of room for original data
        decoded.resize(pad_position, 0);

        for _ in 0..pad_position {
            b64.push('A');
        }
        // finish the quad with padding
        for _ in 0..(4 - (pad_position % 4)) {
            b64.push('=');
        }

        let decode_res = engine.internal_decode(
            b64.as_bytes(),
            &mut decoded[..],
            engine.internal_decoded_len_estimate(b64.len()),
        );
        if pad_position % 4 < 2 {
            // impossible padding
            assert_eq!(
                Err(DecodeSliceError::DecodeError(DecodeError::InvalidByte(
                    pad_position,
                    PAD_BYTE
                ))),
                decode_res
            );
        } else {
            let decoded_bytes = pad_position / 4 * 3
                + match pad_position % 4 {
                    0 => 0,
                    2 => 1,
                    3 => 2,
                    _ => unreachable!(),
                };
            assert_eq!(
                Ok(DecodeMetadata::new(decoded_bytes, Some(pad_position))),
                decode_res
            );
        }
    }
}

#[apply(all_engines)]
fn decode_length_estimate_delta<E: EngineWrapper>(engine_wrapper: E) {
    for engine in [E::standard(), E::standard_unpadded()] {
        for &padding in &[truefalse] {
            for orig_len in 0..1000 {
                let encoded_len = encoded_len(orig_len, padding).unwrap();

                let decoded_estimate = engine
                    .internal_decoded_len_estimate(encoded_len)
                    .decoded_len_estimate();
                assert!(decoded_estimate >= orig_len);
                assert!(
                    decoded_estimate - orig_len < 3,
                    "estimate: {}, encoded: {}, orig: {}",
                    decoded_estimate,
                    encoded_len,
                    orig_len
                );
            }
        }
    }
}

#[apply(all_engines)]
fn estimate_via_u128_inflation<E: EngineWrapper>(engine_wrapper: E) {
    // cover both ends of usize
    (0..1000)
        .chain(usize::MAX - 1000..=usize::MAX)
        .for_each(|encoded_len| {
            // inflate to 128 bit type to be able to safely use the easy formulas
            let len_128 = encoded_len as u128;

            let estimate = E::standard()
                .internal_decoded_len_estimate(encoded_len)
                .decoded_len_estimate();

            // This check is a little too strict: it requires using the (len + 3) / 4 * 3 formula
            // or equivalent, but until other engines come along that use a different formula
            // requiring that we think more carefully about what the allowable criteria are, this
            // will do.
            assert_eq!(
                ((len_128 + 3) / 4 * 3as usize,
                estimate,
                "enc len {}",
                encoded_len
            );
        })
}

#[apply(all_engines)]
fn decode_slice_checked_fails_gracefully_at_all_output_lengths<E: EngineWrapper>(
    engine_wrapper: E,
) {
    let mut rng = seeded_rng();
    for original_len in 0..1000 {
        let mut original = vec![0; original_len];
        rng.fill(&mut original[..]);

        for mode in all_pad_modes() {
            let engine = E::standard_with_pad_mode(
                match mode {
                    DecodePaddingMode::Indifferent | DecodePaddingMode::RequireCanonical => true,
                    DecodePaddingMode::RequireNone => false,
                },
                mode,
            );

            let encoded = engine.encode(&original);
            let mut decode_buf = Vec::with_capacity(original_len);
            for decode_buf_len in 0..original_len {
                decode_buf.resize(decode_buf_len, 0);
                assert_eq!(
                    DecodeSliceError::OutputSliceTooSmall,
                    engine
                        .decode_slice(&encoded, &mut decode_buf[..])
                        .unwrap_err(),
                    "original len: {}, encoded len: {}, buf len: {}, mode: {:?}",
                    original_len,
                    encoded.len(),
                    decode_buf_len,
                    mode
                );
                // internal method works the same
                assert_eq!(
                    DecodeSliceError::OutputSliceTooSmall,
                    engine
                        .internal_decode(
                            encoded.as_bytes(),
                            &mut decode_buf[..],
                            engine.internal_decoded_len_estimate(encoded.len())
                        )
                        .unwrap_err()
                );
            }

            decode_buf.resize(original_len, 0);
            rng.fill(&mut decode_buf[..]);
            assert_eq!(
                original_len,
                engine.decode_slice(&encoded, &mut decode_buf[..]).unwrap()
            );
            assert_eq!(original, decode_buf);
        }
    }
}

/// Returns a tuple of the original data length, the encoded data length (just data), and the length including padding.
///
/// Vecs provided should be empty.
fn generate_random_encoded_data<E: Engine, R: rand::Rng, D: distributions::Distribution<usize>>(
    engine: &E,
    orig_data: &mut Vec<u8>,
    encode_buf: &mut Vec<u8>,
    rng: &mut R,
    length_distribution: &D,
) -> (usize, usize, usize) {
    let padding: bool = engine.config().encode_padding();

    let orig_len = fill_rand(orig_data, rng, length_distribution);
    let expected_encoded_len = encoded_len(orig_len, padding).unwrap();
    encode_buf.resize(expected_encoded_len, 0);

    let base_encoded_len = engine.internal_encode(&orig_data[..], &mut encode_buf[..]);

    let enc_len_with_padding = if padding {
        base_encoded_len + add_padding(base_encoded_len, &mut encode_buf[base_encoded_len..])
    } else {
        base_encoded_len
    };

    assert_eq!(expected_encoded_len, enc_len_with_padding);

    (orig_len, base_encoded_len, enc_len_with_padding)
}

// fill to a random length
fn fill_rand<R: rand::Rng, D: distributions::Distribution<usize>>(
    vec: &mut Vec<u8>,
    rng: &mut R,
    length_distribution: &D,
) -> usize {
    let len = length_distribution.sample(rng);
    for _ in 0..len {
        vec.push(rng.gen());
    }

    len
}

fn fill_rand_len<R: rand::Rng>(vec: &mut Vec<u8>, rng: &mut R, len: usize) {
    for _ in 0..len {
        vec.push(rng.gen());
    }
}

fn prefixed_data<'i>(input_with_prefix: &'mut String, prefix_len: usize, data: &str) -> &'i str {
    input_with_prefix.truncate(prefix_len);
    input_with_prefix.push_str(data);
    input_with_prefix.as_str()
}

/// A wrapper to make using engines in rstest fixtures easier.
/// The functions don't need to be instance methods, but rstest does seem
/// to want an instance, so instances are passed to test functions and then ignored.
trait EngineWrapper {
    type Engine: Engine;

    /// Return an engine configured for RFC standard base64
    fn standard() -> Self::Engine;

    /// Return an engine configured for RFC standard base64, except with no padding appended on
    /// encode, and required no padding on decode.
    fn standard_unpadded() -> Self::Engine;

    /// Return an engine configured for RFC standard alphabet with the provided encode and decode
    /// pad settings
    fn standard_with_pad_mode(encode_pad: bool, decode_pad_mode: DecodePaddingMode)
        -> Self::Engine;

    /// Return an engine configured for RFC standard base64 that allows invalid trailing bits
    fn standard_allow_trailing_bits() -> Self::Engine;

    /// Return an engine configured with a randomized alphabet and config
    fn random<R: rand::Rng>(rng: &mut R) -> Self::Engine;

    /// Return an engine configured with the specified alphabet and randomized config
    fn random_alphabet<R: rand::Rng>(rng: &mut R, alphabet: &Alphabet) -> Self::Engine;
}

struct GeneralPurposeWrapper {}

impl EngineWrapper for GeneralPurposeWrapper {
    type Engine = general_purpose::GeneralPurpose;

    fn standard() -> Self::Engine {
        general_purpose::GeneralPurpose::new(&STANDARD, general_purpose::PAD)
    }

    fn standard_unpadded() -> Self::Engine {
        general_purpose::GeneralPurpose::new(&STANDARD, general_purpose::NO_PAD)
    }

    fn standard_with_pad_mode(
        encode_pad: bool,
        decode_pad_mode: DecodePaddingMode,
    ) -> Self::Engine {
        general_purpose::GeneralPurpose::new(
            &STANDARD,
            general_purpose::GeneralPurposeConfig::new()
                .with_encode_padding(encode_pad)
                .with_decode_padding_mode(decode_pad_mode),
        )
    }

    fn standard_allow_trailing_bits() -> Self::Engine {
        general_purpose::GeneralPurpose::new(
            &STANDARD,
            general_purpose::GeneralPurposeConfig::new().with_decode_allow_trailing_bits(true),
        )
    }

    fn random<R: rand::Rng>(rng: &mut R) -> Self::Engine {
        let alphabet = random_alphabet(rng);

        Self::random_alphabet(rng, alphabet)
    }

    fn random_alphabet<R: rand::Rng>(rng: &mut R, alphabet: &Alphabet) -> Self::Engine {
        general_purpose::GeneralPurpose::new(alphabet, random_config(rng))
    }
}

struct NaiveWrapper {}

impl EngineWrapper for NaiveWrapper {
    type Engine = naive::Naive;

    fn standard() -> Self::Engine {
        naive::Naive::new(
            &STANDARD,
            naive::NaiveConfig {
                encode_padding: true,
                decode_allow_trailing_bits: false,
                decode_padding_mode: DecodePaddingMode::RequireCanonical,
            },
        )
    }

    fn standard_unpadded() -> Self::Engine {
        naive::Naive::new(
            &STANDARD,
            naive::NaiveConfig {
                encode_padding: false,
                decode_allow_trailing_bits: false,
                decode_padding_mode: DecodePaddingMode::RequireNone,
            },
        )
    }

    fn standard_with_pad_mode(
        encode_pad: bool,
        decode_pad_mode: DecodePaddingMode,
    ) -> Self::Engine {
        naive::Naive::new(
            &STANDARD,
            naive::NaiveConfig {
                encode_padding: encode_pad,
                decode_allow_trailing_bits: false,
                decode_padding_mode: decode_pad_mode,
            },
        )
    }

    fn standard_allow_trailing_bits() -> Self::Engine {
        naive::Naive::new(
            &STANDARD,
            naive::NaiveConfig {
                encode_padding: true,
                decode_allow_trailing_bits: true,
                decode_padding_mode: DecodePaddingMode::RequireCanonical,
            },
        )
    }

    fn random<R: rand::Rng>(rng: &mut R) -> Self::Engine {
        let alphabet = random_alphabet(rng);

        Self::random_alphabet(rng, alphabet)
    }

    fn random_alphabet<R: rand::Rng>(rng: &mut R, alphabet: &Alphabet) -> Self::Engine {
        let mode = rng.gen();

        let config = naive::NaiveConfig {
            encode_padding: match mode {
                DecodePaddingMode::Indifferent => rng.gen(),
                DecodePaddingMode::RequireCanonical => true,
                DecodePaddingMode::RequireNone => false,
            },
            decode_allow_trailing_bits: rng.gen(),
            decode_padding_mode: mode,
        };

        naive::Naive::new(alphabet, config)
    }
}

/// A pseudo-Engine that routes all decoding through [DecoderReader]
struct DecoderReaderEngine<E: Engine> {
    engine: E,
}

impl<E: Engine> From<E> for DecoderReaderEngine<E> {
    fn from(value: E) -> Self {
        Self { engine: value }
    }
}

impl<E: Engine> Engine for DecoderReaderEngine<E> {
    type Config = E::Config;
    type DecodeEstimate = E::DecodeEstimate;

    fn internal_encode(&self, input: &[u8], output: &mut [u8]) -> usize {
        self.engine.internal_encode(input, output)
    }

    fn internal_decoded_len_estimate(&self, input_len: usize) -> Self::DecodeEstimate {
        self.engine.internal_decoded_len_estimate(input_len)
    }

    fn internal_decode(
        &self,
        input: &[u8],
        output: &mut [u8],
        decode_estimate: Self::DecodeEstimate,
    ) -> Result<DecodeMetadata, DecodeSliceError> {
        let mut reader = DecoderReader::new(input, &self.engine);
        let mut buf = vec![0; input.len()];
        // to avoid effects like not detecting invalid length due to progressively growing
        // the output buffer in read_to_end etc, read into a big enough buffer in one go
        // to make behavior more consistent with normal engines
        let _ = reader
            .read(&mut buf)
            .and_then(|len| {
                buf.truncate(len);
                // make sure we got everything
                reader.read_to_end(&mut buf)
            })
            .map_err(|io_error| {
                *io_error
                    .into_inner()
                    .and_then(|inner| inner.downcast::<DecodeError>().ok())
                    .unwrap()
            })?;
        if output.len() < buf.len() {
            return Err(DecodeSliceError::OutputSliceTooSmall);
        }
        output[..buf.len()].copy_from_slice(&buf);
        Ok(DecodeMetadata::new(
            buf.len(),
            input
                .iter()
                .enumerate()
                .filter(|(_offset, byte)| **byte == PAD_BYTE)
                .map(|(offset, _byte)| offset)
                .next(),
        ))
    }

    fn config(&self) -> &Self::Config {
        self.engine.config()
    }
}

struct DecoderReaderEngineWrapper {}

impl EngineWrapper for DecoderReaderEngineWrapper {
    type Engine = DecoderReaderEngine<general_purpose::GeneralPurpose>;

    fn standard() -> Self::Engine {
        GeneralPurposeWrapper::standard().into()
    }

    fn standard_unpadded() -> Self::Engine {
        GeneralPurposeWrapper::standard_unpadded().into()
    }

    fn standard_with_pad_mode(
        encode_pad: bool,
        decode_pad_mode: DecodePaddingMode,
    ) -> Self::Engine {
        GeneralPurposeWrapper::standard_with_pad_mode(encode_pad, decode_pad_mode).into()
    }

    fn standard_allow_trailing_bits() -> Self::Engine {
        GeneralPurposeWrapper::standard_allow_trailing_bits().into()
    }

    fn random<R: rand::Rng>(rng: &mut R) -> Self::Engine {
        GeneralPurposeWrapper::random(rng).into()
    }

    fn random_alphabet<R: rand::Rng>(rng: &mut R, alphabet: &Alphabet) -> Self::Engine {
        GeneralPurposeWrapper::random_alphabet(rng, alphabet).into()
    }
}

fn seeded_rng() -> impl rand::Rng {
    rngs::SmallRng::from_entropy()
}

fn all_pad_modes() -> Vec<DecodePaddingMode> {
    vec![
        DecodePaddingMode::Indifferent,
        DecodePaddingMode::RequireCanonical,
        DecodePaddingMode::RequireNone,
    ]
}

fn pad_modes_allowing_padding() -> Vec<DecodePaddingMode> {
    vec![
        DecodePaddingMode::Indifferent,
        DecodePaddingMode::RequireCanonical,
    ]
}

fn assert_all_suffixes_ok<E: Engine>(engine: E, suffixes: Vec<&str>) {
    for num_prefix_quads in 0..256 {
        for &suffix in suffixes.iter() {
            let mut encoded = "AAAA".repeat(num_prefix_quads);
            encoded.push_str(suffix);

            let res = &engine.decode(&encoded);
            assert!(res.is_ok());
        }
    }
}

Messung V0.5 in Prozent
C=92 H=100 G=95

¤ Dauer der Verarbeitung: 0.23 Sekunden  (vorverarbeitet am  2026-08-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=661743