use base64::Engine;
use bstr::{BString, ByteVec};
pub(crate) fn base64_patterns(
s: &[u8],
alphabet: Option<&str>,
) -> Vec<(u8, BString)> {
assert!(s.len() > 1);
let alphabet = alphabet.map_or(base64::alphabet::STANDARD, |a| {
base64::alphabet::Alphabet::new(a).unwrap()
});
let base64_engine = base64::engine::GeneralPurpose::new(
&alphabet,
base64::engine::general_purpose::NO_PAD,
);
let mut pattern: Vec<u8> = Vec::with_capacity(3 + s.len());
pattern.push_str("XX");
pattern.push_str(s);
let mut base64_patterns = Vec::new();
let mut buf = vec![0; base64::encoded_len(pattern.len(), false).unwrap()];
for i in 0..=2_u8 {
let pattern = &pattern[i as usize..];
let base64_len =
base64_engine.encode_slice(pattern, &mut buf).unwrap();
buf.truncate(base64_len);
let right_trim = usize::from(!pattern.len().is_multiple_of(3));
let range = match i {
0 => 3..base64_len - right_trim,
1 => 2..base64_len - right_trim,
2 => 0..base64_len - right_trim,
_ => unreachable!(),
};
base64_patterns.push((2 - i, BString::from(&buf[range])));
}
base64_patterns
}
#[cfg(test)]
mod test {
use super::base64_patterns;
use bstr::BString;
use pretty_assertions::assert_eq;
#[test]
fn base64() {
assert_eq!(
base64_patterns(b"fo", None),
vec![
(2, BString::from("mb")),
(1, BString::from("Zv")),
(0, BString::from("Zm"))
]
);
assert_eq!(
base64_patterns(b"foo", None),
vec![
(2, BString::from("mb2")),
(1, BString::from("Zvb")),
(0, BString::from("Zm9v")),
]
);
assert_eq!(
base64_patterns(b"foob", None),
vec![
(2, BString::from("mb29i")),
(1, BString::from("Zvb2")),
(0, BString::from("Zm9vY"))
]
);
assert_eq!(
base64_patterns(b"fooba", None),
vec![
(2, BString::from("mb29iY")),
(1, BString::from("Zvb2Jh")),
(0, BString::from("Zm9vYm"))
]
);
assert_eq!(
base64_patterns(b"foobar", None),
vec![
(2, BString::from("mb29iYX")),
(1, BString::from("Zvb2Jhc")),
(0, BString::from("Zm9vYmFy"))
]
);
assert_eq!(
base64_patterns(
b"foobar",
Some(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
)
),
vec![
(2, BString::from("mb29iYX")),
(1, BString::from("Zvb2Jhc")),
(0, BString::from("Zm9vYmFy"))
]
);
assert_eq!(
base64_patterns(
b"foobar",
Some(
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
)
),
vec![
(2, BString::from("kZ07gWV")),
(1, BString::from("XtZ0Hfa")),
(0, BString::from("Xk7tWkDw"))
]
);
}
}