use crate::{ConstInit, PhantomData, whilst};
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
#[derive(Clone, Copy, Debug)]
pub struct Base<
const RADIX: usize, const LUT: bool, const PAD: bool, const CASE: bool, CODE, > {
_code: PhantomData<CODE>,
}
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
pub type Base16 = Base<16, false, false, false, Rfc4648>;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
pub type Base32 = Base<32, true, false, false, Rfc4648>;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
pub type Base32Padded = Base<32, true, true, false, Rfc4648>;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
pub type Base32Crockford = Base<32, true, false, true, Crockford>;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
pub type Base32Hex = Base<32, true, false, false, Rfc4648Hex>;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
pub type Base64 = Base<64, true, false, false, Rfc4648>;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
pub type Base64Padded = Base<64, true, true, false, Rfc4648>;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
#[derive(Clone, Copy, Debug)]
pub struct Rfc4648;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
#[derive(Clone, Copy, Debug)]
pub struct Rfc4648Hex;
#[doc = crate::_tags!(codec)]
#[doc = crate::_doc_location!("data/codec")]
#[derive(Clone, Copy, Debug)]
pub struct Crockford;
crate::sf! {
impl<const LUT: bool, const PAD: bool, const CASE: bool, CODE>
Base<16, LUT, PAD, CASE, CODE> {
const ALPHABET: [u8; 16] = *b"0123456789ABCDEF";
const fn remap(byte: u8) -> Option<u8> { Some(byte) } }
impl<const PAD: bool, const CASE: bool> Base<16, false, PAD, CASE, Rfc4648> {
methods!(16, false, 4, Self); } impl<const PAD: bool, const CASE: bool>
Base<16, true, PAD, CASE, Rfc4648> { build_lut!(16, Self::remap, Self::ALPHABET);
methods!(16, true, 4, Self); }
impl<const LUT: bool, const PAD: bool, const CASE: bool>
Base<32, LUT, PAD, CASE, Rfc4648> { const ALPHABET: [u8; 32] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; const fn remap(byte: u8) -> Option<u8> { Some(byte) } }
impl<const PAD: bool, const CASE: bool>
Base<32, false, PAD, CASE, Rfc4648> { methods!(32, false, 5, Self); }
impl<const PAD: bool, const CASE: bool>
Base<32, true, PAD, CASE, Rfc4648> { build_lut!(32, Self::remap, Self::ALPHABET);
methods!(32, true, 5, Self); }
impl<const LUT: bool, const PAD: bool, const CASE: bool>
Base<32, LUT, PAD, CASE, Crockford> { const ALPHABET: [u8; 32] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; const fn remap(byte: u8) -> Option<u8> {
match byte { b'O' => Some(b'0'), b'I' | b'L' => Some(b'1'), _ => Some(byte) } } }
impl<const PAD: bool, const CASE: bool>
Base<32, false, PAD, CASE, Crockford> { methods!(32, false, 5, Self); }
impl<const PAD: bool, const CASE: bool>
Base<32, true, PAD, CASE, Crockford> { build_lut!(32, Self::remap, Self::ALPHABET);
methods!(32, true, 5, Self); }
impl<const LUT: bool, const PAD: bool, const CASE: bool>
Base<32, LUT, PAD, CASE, Rfc4648Hex> { const ALPHABET: [u8; 32] = *b"0123456789ABCDEFGHIJKLMNOPQRSTUV"; const fn remap(byte: u8) -> Option<u8> { Some(byte) } }
impl<const PAD: bool, const CASE: bool>
Base<32, false, PAD, CASE, Rfc4648Hex> { methods!(32, false, 5, Self); }
impl<const PAD: bool, const CASE: bool>
Base<32, true, PAD, CASE, Rfc4648Hex> { build_lut!(32, Self::remap, Self::ALPHABET);
methods!(32, true, 5, Self); }
impl<const LUT: bool, const PAD: bool, const CASE: bool>
Base<64, LUT, PAD, CASE, Rfc4648> { const ALPHABET: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const fn remap(byte: u8) -> Option<u8> { Some(byte) } }
impl<const PAD: bool, const CASE: bool>
Base<64, false, PAD, CASE, Rfc4648> { methods!(64, false, 6, Self); }
impl<const PAD: bool, const CASE: bool>
Base<64, true, PAD, CASE, Rfc4648> { build_lut!(64, Self::remap, Self::ALPHABET);
methods!(64, true, 6, Self); }
}
#[rustfmt::skip]
impl<const RADIX: usize, const LUT: bool, const PAD: bool, const CASE: bool, CODE>
Base<RADIX, LUT, PAD, CASE, CODE> {
pub const fn new() -> Self {
Self { _code: PhantomData }
}
pub const fn with_radix<const NEW_RADIX: usize>(self)
-> Base<NEW_RADIX, LUT, PAD, CASE, CODE> { Base { _code: PhantomData }
}
pub const fn with_lut<const NEW_LUT: bool>(self)
-> Base<RADIX, NEW_LUT, PAD, CASE, CODE> { Base { _code: PhantomData }
}
pub const fn with_pad<const NEW_PAD: bool>(self)
-> Base<RADIX, LUT, NEW_PAD, CASE, CODE> { Base { _code: PhantomData }
}
pub const fn with_case<const NEW_CASE: bool>(self)
-> Base<RADIX, LUT, PAD, NEW_CASE, CODE> { Base { _code: PhantomData }
}
pub const fn with_encoding<NewCode>(self)
-> Base<RADIX, LUT, PAD, CASE, NewCode> { Base { _code: PhantomData }
}
}
#[rustfmt::skip]
impl<
const RADIX: usize, const LUT: bool, const PAD: bool, const CASE: bool, CODE,
> ConstInit for Base<RADIX, LUT, PAD, CASE, CODE>
{
const INIT: Self = Self::new();
}
#[rustfmt::skip]
impl<
const RADIX: usize, const LUT: bool, const PAD: bool, const CASE: bool, CODE,
> Default for Base<RADIX, LUT, PAD, CASE, CODE>
{
fn default() -> Self { Self::new() }
}
macro_rules! build_lut {
($radix:expr, $remap_fn:expr, $alphabet:expr) => {
const LUT_TABLE: [u8; 256] = {
let mut table = [255; 256]; whilst! { i in 0..$radix; {
let base_char = $alphabet[i];
if let Some(mapped) = $remap_fn(base_char) {
table[mapped as usize] = i as u8;
}
table[base_char as usize] = i as u8;
if CASE {
table[base_char.to_ascii_lowercase() as usize] = i as u8;
}
}}
table
};
};
}
use build_lut;
macro_rules! methods {
( // LUT: True
$radix:expr, true, $chunk_bits:expr, $Self:ident) => {
const fn decode_byte(byte: u8) -> Option<u8> {
let decoded = $Self::LUT_TABLE[byte as usize];
if decoded == 255 { None } else { Some(decoded) }
}
methods![@non_alloc $radix, $chunk_bits, $Self];
};
( // LUT: False
$radix:expr, false, $chunk_bits:expr, $Self:ident) => {
const fn decode_byte(byte: u8) -> Option<u8> {
let mut i = 0;
while i < $radix {
let mapped = $Self::remap($Self::ALPHABET[i]);
if mapped.is_none() {
i += 1;
continue;
} if byte == mapped.unwrap()
|| (CASE && byte == mapped.unwrap().to_ascii_lowercase()) {
return Some(i as u8);
}
i += 1;
}
None
}
methods![@non_alloc $radix, $chunk_bits, $Self];
};
(@non_alloc $radix:expr, $chunk_bits:expr, $Self:ident) => {
pub const fn encoded_len(input_len: usize) -> usize {
(input_len * 8).div_ceil($chunk_bits)
}
pub const fn encoded_len_padded(input_len: usize) -> usize {
let base_len = (input_len * 8).div_ceil($chunk_bits);
if PAD {
base_len.div_ceil(8) * 8 } else {
base_len
}
}
pub const fn decoded_len(input_len: usize) -> usize { (input_len * $chunk_bits) / 8 }
pub const fn decoded_len_stripped(input: &[u8]) -> usize {
let mut length = input.len();
while length > 0 && input[length - 1] == b'=' {
length -= 1;
}
(length * $chunk_bits) / 8
}
pub const fn decode_from_slice(input: &[u8], output: &mut [u8]) -> Option<usize> {
let (mut buffer, mut bits_left, mut index, mut i): (u32, u8, usize, usize) = (0,0,0,0);
while i < input.len() {
let byte = input[i];
if PAD && byte == b'=' { break; } let Some(value) = $Self::decode_byte(byte) else { return None };
buffer = (buffer << $chunk_bits) | value as u32;
bits_left += $chunk_bits;
i += 1;
while bits_left >= 8 {
output[index] = (buffer >> (bits_left - 8)) as u8;
bits_left -= 8;
index += 1;
}
}
Some(index)
}
pub const fn encode_to_slice(input: &[u8], output: &mut [u8]) -> usize {
let (mut buffer, mut bits_left, mut index, mut i): (u32, u8, usize, usize) = (0,0,0,0);
while i < input.len() {
buffer = (buffer << 8) | input[i] as u32;
bits_left += 8;
i += 1;
while bits_left >= $chunk_bits as u8 {
let char_index = ((buffer >> (bits_left - $chunk_bits as u8))
& ((1 << $chunk_bits) - 1)) as usize;
output[index] = $Self::ALPHABET[char_index];
bits_left -= $chunk_bits as u8;
index += 1;
}
}
if bits_left > 0 {
let char_index = ((buffer << ($chunk_bits as u8 - bits_left))
& ((1 << $chunk_bits) - 1)) as usize;
output[index] = $Self::ALPHABET[char_index];
index += 1;
}
if PAD {
while index % 8 != 0 {
output[index] = b'=';
index += 1;
}
}
index
}
};
}
use methods;