use std::any::Any;
use base64::Engine as _;
mod base32_codecs;
mod base64_codecs;
mod common_encodings;
mod ctf_codecs;
mod encoding_rs_codecs;
pub type Encoded = Box<[u8]>;
pub type MaybeDecoded = Option<Box<[u8]>>;
pub trait ThreadSafeAny: Any + Send + Sync {}
impl<T> ThreadSafeAny for T where T: Any + Send + Sync {}
pub type DecoderMetadata = Option<&'static dyn ThreadSafeAny>;
pub type DecoderName = &'static str;
pub struct Codec {
pub encoded: Encoded,
pub name: &'static str,
pub decoder: fn(Encoded, DecoderMetadata) -> MaybeDecoded,
pub metadata: DecoderMetadata,
}
pub type CodecGenerator = fn(&str) -> Vec<Codec>;
fn retrieve_metadata<T>(meta: DecoderMetadata) -> &'static T {
let meta_ref = meta.expect("metadata was missing");
(meta_ref as &dyn Any)
.downcast_ref()
.expect("metadata was the wrong type")
}
pub const ALL_CODEC_GENERATORS: &'static [CodecGenerator] = &[
common_encodings::common_encodings,
base64_codecs::base64_codecs,
base32_codecs::base32_codecs,
ctf_codecs::ctf_codecs,
encoding_rs_codecs::encoding_rs_codecs,
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_codecs_roundtrip_to_prefix() {
let mut random_data = Vec::with_capacity(999);
random_data.resize(random_data.capacity(), 0);
for b in random_data.iter_mut() {
*b = rand::random_range(b' '..=b'~');
}
let random_str = unsafe { std::str::from_utf8_unchecked(&random_data) };
if !cfg!(debug_assertions) {
crate::GAMBLE.with(|gamble| gamble.store(true, std::sync::atomic::Ordering::Relaxed));
}
for codec_generator in ALL_CODEC_GENERATORS {
for codec in codec_generator(random_str) {
let Codec {
encoded,
name,
decoder,
metadata,
} = codec;
let decoded = &decoder(encoded.clone(), metadata).unwrap()[..];
for (dec, cor) in decoded.iter().zip(&random_data) {
assert_eq!(dec, cor, "Codec {} roundtrip failed", name);
}
}
}
}
}