base64url_nopad 0.2.0

Fast, efficient, correct, and const base64url without padding encoding and decoding
Documentation
use super::DecodeErr;
#[cfg(any(
    target_pointer_width = "16",
    target_pointer_width = "32",
    target_pointer_width = "64",
))]
use super::MAX_ENCODE_INPUT_LEN;
#[cfg(feature = "alloc")]
use alloc::{borrow::ToOwned as _, string::String, vec};
use core::fmt::{self, Write};
#[cfg(any(
    target_pointer_width = "16",
    target_pointer_width = "32",
    target_pointer_width = "64",
))]
use rand::{RngExt as _, rngs::SmallRng};
#[expect(
    clippy::as_conversions,
    clippy::cast_possible_truncation,
    reason = "comment justifies correctness"
)]
#[cfg(any(
    target_pointer_width = "16",
    target_pointer_width = "32",
    target_pointer_width = "64",
))]
#[ignore = "slow"]
#[test]
fn encode_decode_len() {
    assert_eq!(MAX_ENCODE_INPUT_LEN, 3 * (usize::MAX.div_ceil(4)) - 1);
    let mut rng = rand::make_rng::<SmallRng>();
    for _ in 0u32..10_000_000 {
        // `uN as usize` is fine since we `cfg` by pointer width.
        #[cfg(target_pointer_width = "16")]
        let len = rng.random::<u16>() as usize;
        #[cfg(target_pointer_width = "32")]
        let len = rng.random::<u32>() as usize;
        #[cfg(target_pointer_width = "64")]
        let len = rng.random::<u64>() as usize;
        if len <= MAX_ENCODE_INPUT_LEN {
            assert_eq!(
                super::encode_len_checked(len).map(super::decode_len),
                Some(Some(len))
            );
        } else {
            assert!(super::encode_len_checked(len).is_none());
        }
    }
    for i in 0..1025 {
        assert_eq!(
            super::encode_len_checked(i).map(super::decode_len),
            Some(Some(i))
        );
    }
    #[cfg(target_pointer_width = "16")]
    for i in MAX_ENCODE_INPUT_LEN + 1.. {
        assert!(super::encode_len_checked(i).is_none());
    }
    #[cfg(not(target_pointer_width = "16"))]
    for i in MAX_ENCODE_INPUT_LEN + 1..MAX_ENCODE_INPUT_LEN + 1_000_000 {
        assert!(super::encode_len_checked(i).is_none());
    }
    assert!(super::encode_len_checked(usize::MAX).is_none());
    assert_eq!(
        super::encode_len_checked(MAX_ENCODE_INPUT_LEN),
        Some(usize::MAX)
    );
    for _ in 0u32..10_000_000 {
        #[cfg(target_pointer_width = "16")]
        let len = rng.random::<u16>() as usize;
        #[cfg(target_pointer_width = "32")]
        let len = rng.random::<u32>() as usize;
        #[cfg(target_pointer_width = "64")]
        let len = rng.random::<u64>() as usize;
        if len & 3 == 1 {
            assert!(super::decode_len(len).is_none());
        } else {
            assert_eq!(
                super::decode_len(len).map(super::encode_len_checked),
                Some(Some(len))
            );
        }
    }
    for i in 0..1025 {
        if i & 3 == 1 {
            assert!(super::decode_len(i).is_none());
        } else {
            assert_eq!(
                super::decode_len(i).map(super::encode_len_checked),
                Some(Some(i))
            );
        }
    }
    #[cfg(target_pointer_width = "16")]
    for i in 0..=usize::MAX {
        if i & 3 == 1 {
            assert!(super::decode_len(i).is_none());
        } else {
            assert_eq!(
                super::decode_len(i).map(super::encode_len_checked),
                Some(Some(i))
            );
        }
    }
    #[cfg(not(target_pointer_width = "16"))]
    for i in usize::MAX - 1_000_000..=usize::MAX {
        if i & 3 == 1 {
            assert!(super::decode_len(i).is_none());
        } else {
            assert_eq!(
                super::decode_len(i).map(super::encode_len_checked),
                Some(Some(i))
            );
        }
    }
    assert_eq!(super::decode_len(usize::MAX), Some(MAX_ENCODE_INPUT_LEN));
}
#[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
#[cfg(feature = "alloc")]
#[test]
fn encode_write() {
    let input = [9; 8192];
    let mut buffer = String::with_capacity(super::encode_len(input.len()));
    let cap = buffer.capacity();
    let mut write_len;
    for len in 0..input.len() {
        write_len = super::encode_len(len);
        match write_len.checked_add(buffer.len()) {
            None => {
                buffer.clear();
                // Indexing is fine since `len <= input.len()`.
                assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
                assert_eq!(buffer.len(), write_len);
            }
            Some(l) => {
                if l > cap {
                    buffer.clear();
                    // Indexing is fine since `len <= input.len()`.
                    assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
                    assert_eq!(buffer.len(), write_len);
                } else {
                    // Indexing is fine since `len <= input.len()`.
                    assert_eq!(super::encode_write(&input[..len], &mut buffer), Ok(()));
                    assert_eq!(buffer.len(), l);
                }
            }
        }
        assert!(
            buffer
                .as_bytes()
                .iter()
                .all(|b| { matches!(*b, b'C' | b'J' | b'Q' | b'k') })
        );
    }
}
#[cfg(feature = "alloc")]
#[test]
fn try_encode() {
    assert_eq!(super::try_encode(&[0]), Ok("AA".to_owned()));
}
#[cfg(feature = "alloc")]
#[test]
fn encode() {
    assert_eq!(super::encode(&[0]), "AA".to_owned());
}
#[cfg(feature = "alloc")]
#[test]
fn try_encode_append() {
    let mut val = "Hi".to_owned();
    assert_eq!(
        super::try_encode_append(&[0], &mut val),
        Ok("AA".to_owned().as_mut_str())
    );
    assert_eq!(val, "HiAA");
}
#[cfg(feature = "alloc")]
#[test]
fn encode_append() {
    let mut val = "Hi".to_owned();
    assert_eq!(
        super::encode_append(&[0], &mut val),
        "AA".to_owned().as_mut_str()
    );
    assert_eq!(val, "HiAA");
}
#[cfg(feature = "alloc")]
#[test]
fn try_encode_append_only() {
    let mut val = "Hi".to_owned();
    assert_eq!(super::try_encode_append_only(&[0], &mut val), Ok(()));
    assert_eq!(val, "HiAA");
}
#[cfg(feature = "alloc")]
#[test]
fn encode_append_only() {
    let mut val = "Hi".to_owned();
    super::encode_append_only(&[0], &mut val);
    assert_eq!(val, "HiAA");
}
#[test]
fn decode_buffer() {
    assert_eq!(
        super::decode_buffer(b"AA", &mut [0]),
        Ok([0].as_mut_slice())
    );
    assert_eq!(
        super::decode_buffer(b"AA", &mut []),
        Err(DecodeErr::BufferLen)
    );
}
#[cfg(feature = "alloc")]
#[test]
fn decode() {
    assert_eq!(super::decode(b"AA"), Ok(vec![0]));
}
#[test]
fn validate_encoded_data() {
    assert_eq!(super::validate_encoded_data(b"AA"), Ok(()));
}
#[test]
fn encode_buffer_exact() {
    let mut output = [0; 2];
    super::encode_buffer_exact(&[0], &mut output);
    assert_eq!(output, *b"AA");
}
#[test]
fn decode_buffer_exact() {
    let mut output = [1];
    assert_eq!(super::decode_buffer_exact(b"AA", &mut output), Ok(()));
    assert_eq!(output, [0]);
}
#[test]
fn encode_write_core() {
    struct Writer<'a>(&'a mut [u8]);
    impl Write for Writer<'_> {
        fn write_str(&mut self, s: &str) -> fmt::Result {
            self.0.get_mut(..s.len()).ok_or(fmt::Error).map(|bytes| {
                bytes.copy_from_slice(s.as_bytes());
            })
        }
    }
    let mut writer = Writer(&mut [0, 0, 1]);
    assert_eq!(super::encode_write(&[0], &mut writer), Ok(()));
    assert_eq!(writer.0, b"AA\x01");
    assert_eq!(
        super::encode_write(&[0, 0, 0], &mut writer),
        Err(fmt::Error)
    );
}