use crate::error::*;
use crate::{Decoder, Encoder};
struct Base32Impl;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Base32Variant {
Standard = 1,
StandardNoPadding = 3,
Hex = 5,
HexNoPadding = 7,
}
enum VariantMask {
NoPadding = 2,
Hex = 4,
}
impl Base32Impl {
#[inline]
fn _eq(x: u8, y: u8) -> u8 {
!(((0u16.wrapping_sub((x as u16) ^ (y as u16))) >> 8) as u8)
}
#[inline]
fn _gt(x: u8, y: u8) -> u8 {
(((y as u16).wrapping_sub(x as u16)) >> 8) as u8
}
#[inline]
fn _ge(x: u8, y: u8) -> u8 {
!Self::_gt(y, x)
}
#[inline]
fn _lt(x: u8, y: u8) -> u8 {
Self::_gt(y, x)
}
#[inline]
fn _le(x: u8, y: u8) -> u8 {
Self::_ge(y, x)
}
#[inline]
fn b32_byte_to_char(x: u8) -> u8 {
(Self::_lt(x, 26) & (x.wrapping_add(b'A')))
| (Self::_ge(x, 26) & Self::_lt(x, 32) & (x.wrapping_add(b'2'.wrapping_sub(26))))
}
#[inline]
fn b32_char_to_byte(c: u8) -> u8 {
let x = (Self::_ge(c, b'A') & Self::_le(c, b'Z') & (c.wrapping_sub(b'A')))
| (Self::_ge(c, b'2') & Self::_le(c, b'7') & (c.wrapping_sub(b'2').wrapping_add(26)));
x | (Self::_eq(x, 0) & Self::_eq(c, b'A') ^ 0xff)
}
#[inline]
fn b32_hex_byte_to_char(x: u8) -> u8 {
(Self::_lt(x, 10) & (x.wrapping_add(b'0')))
| (Self::_ge(x, 10) & Self::_lt(x, 32) & (x.wrapping_add(b'A'.wrapping_sub(10))))
}
#[inline]
fn b32_hex_char_to_byte(c: u8) -> u8 {
let x = (Self::_ge(c, b'0') & Self::_le(c, b'9') & (c.wrapping_sub(b'0')))
| (Self::_ge(c, b'A') & Self::_le(c, b'V') & (c.wrapping_sub(b'A').wrapping_add(10)))
| (Self::_ge(c, b'a') & Self::_le(c, b'v') & (c.wrapping_sub(b'a').wrapping_add(10)));
x | (Self::_eq(x, 0) & ((Self::_eq(c, b'0') | Self::_eq(c, b'A') | Self::_eq(c, b'a')) ^ 0xff))
}
#[inline]
fn encoded_len(bin_len: usize, variant: Base32Variant) -> Result<usize, Error> {
let bits = bin_len * 8;
let chars = (bits + 4) / 5;
if (variant as u16 & VariantMask::NoPadding as u16) != 0 {
return Ok(chars);
}
let padded_len = (chars + 7) & !7;
Ok(padded_len)
}
pub fn encode<'t>(
b32: &'t mut [u8],
bin: &[u8],
variant: Base32Variant,
) -> Result<&'t [u8], Error> {
let bin_len = bin.len();
let b32_maxlen = b32.len();
let mut b32_pos = 0usize;
let mut bits_left = 0u8;
let mut bits = 0u16;
let is_hex = (variant as u16 & VariantMask::Hex as u16) != 0;
let encoded_len = Self::encoded_len(bin_len, variant)?;
if b32_maxlen < encoded_len {
return Err(Error::Overflow);
}
for &byte in bin {
bits = (bits << 8) | (byte as u16);
bits_left += 8;
while bits_left >= 5 {
bits_left -= 5;
let chunk = ((bits >> bits_left) & 0x1F) as u8;
b32[b32_pos] = if is_hex {
Self::b32_hex_byte_to_char(chunk)
} else {
Self::b32_byte_to_char(chunk)
};
b32_pos += 1;
}
}
if bits_left > 0 {
let chunk = ((bits << (5 - bits_left)) & 0x1F) as u8;
b32[b32_pos] = if is_hex {
Self::b32_hex_byte_to_char(chunk)
} else {
Self::b32_byte_to_char(chunk)
};
b32_pos += 1;
}
if (variant as u16 & VariantMask::NoPadding as u16) == 0 {
while b32_pos < encoded_len {
b32[b32_pos] = b'=';
b32_pos += 1;
}
}
Ok(&b32[..b32_pos])
}
fn skip_padding<'t>(
b32: &'t [u8],
mut padding_len: usize,
ignore: Option<&[u8]>,
) -> Result<&'t [u8], Error> {
let b32_len = b32.len();
let mut b32_pos = 0usize;
while padding_len > 0 {
if b32_pos >= b32_len {
return Err(Error::InvalidInput);
}
let c = b32[b32_pos];
if c == b'=' {
padding_len -= 1
} else {
match ignore {
Some(ignore) if ignore.contains(&c) => {}
_ => return Err(Error::InvalidInput),
}
}
b32_pos += 1
}
Ok(&b32[b32_pos..])
}
pub fn decode<'t>(
bin: &'t mut [u8],
b32: &[u8],
ignore: Option<&[u8]>,
variant: Base32Variant,
) -> Result<&'t [u8], Error> {
let bin_maxlen = bin.len();
let is_hex = (variant as u16 & VariantMask::Hex as u16) != 0;
let mut acc = 0u16;
let mut acc_len = 0usize;
let mut bin_pos = 0usize;
let mut premature_end = None;
for (b32_pos, &c) in b32.iter().enumerate() {
if let Some(ignore_chars) = ignore {
if ignore_chars.contains(&c) {
continue;
}
}
if c == b'=' {
premature_end = Some(b32_pos);
break;
}
let d = if is_hex {
match c {
b'0'..=b'9' => c - b'0',
b'A'..=b'V' => c - b'A' + 10,
b'a'..=b'v' => c - b'a' + 10,
_ => 0xff,
}
} else {
match c {
b'A'..=b'Z' => c - b'A',
b'2'..=b'7' => c - b'2' + 26,
_ => 0xff,
}
};
if d == 0xff {
match ignore {
Some(ignore) if ignore.contains(&c) => continue,
_ => {
return Err(Error::InvalidInput);
}
}
}
acc = (acc << 5) | (d as u16);
acc_len += 5;
if acc_len >= 8 {
acc_len -= 8;
if bin_pos >= bin_maxlen {
return Err(Error::Overflow);
}
bin[bin_pos] = (acc >> acc_len) as u8;
bin_pos += 1;
}
}
if acc_len > 0 && acc_len < 5 && (acc & ((1u16 << acc_len).wrapping_sub(1))) != 0 {
return Err(Error::InvalidInput);
}
if let Some(premature_end) = premature_end {
if variant as u16 & VariantMask::NoPadding as u16 == 0 {
let mut padding_count = 0;
for &c in &b32[premature_end..] {
if c == b'=' {
padding_count += 1;
} else if let Some(ignore_chars) = ignore {
if !ignore_chars.contains(&c) {
return Err(Error::InvalidInput);
}
} else {
return Err(Error::InvalidInput);
}
}
if premature_end == 8 && padding_count != 6 { return Err(Error::InvalidInput);
}
}
}
Ok(&bin[..bin_pos])
}
}
pub struct Base32;
pub struct Base32NoPadding;
pub struct Base32Hex;
pub struct Base32HexNoPadding;
impl Encoder for Base32 {
#[inline]
fn encoded_len(bin_len: usize) -> Result<usize, Error> {
Base32Impl::encoded_len(bin_len, Base32Variant::Standard)
}
#[inline]
fn encode<IN: AsRef<[u8]>>(b32: &mut [u8], bin: IN) -> Result<&[u8], Error> {
Base32Impl::encode(b32, bin.as_ref(), Base32Variant::Standard)
}
}
impl Decoder for Base32 {
#[inline]
fn decode<'t, IN: AsRef<[u8]>>(
bin: &'t mut [u8],
b32: IN,
ignore: Option<&[u8]>,
) -> Result<&'t [u8], Error> {
Base32Impl::decode(bin, b32.as_ref(), ignore, Base32Variant::Standard)
}
}
impl Encoder for Base32NoPadding {
#[inline]
fn encoded_len(bin_len: usize) -> Result<usize, Error> {
Base32Impl::encoded_len(bin_len, Base32Variant::StandardNoPadding)
}
#[inline]
fn encode<IN: AsRef<[u8]>>(b32: &mut [u8], bin: IN) -> Result<&[u8], Error> {
Base32Impl::encode(b32, bin.as_ref(), Base32Variant::StandardNoPadding)
}
}
impl Decoder for Base32NoPadding {
#[inline]
fn decode<'t, IN: AsRef<[u8]>>(
bin: &'t mut [u8],
b32: IN,
ignore: Option<&[u8]>,
) -> Result<&'t [u8], Error> {
Base32Impl::decode(bin, b32.as_ref(), ignore, Base32Variant::StandardNoPadding)
}
}
impl Encoder for Base32Hex {
#[inline]
fn encoded_len(bin_len: usize) -> Result<usize, Error> {
Base32Impl::encoded_len(bin_len, Base32Variant::Hex)
}
#[inline]
fn encode<IN: AsRef<[u8]>>(b32: &mut [u8], bin: IN) -> Result<&[u8], Error> {
Base32Impl::encode(b32, bin.as_ref(), Base32Variant::Hex)
}
}
impl Decoder for Base32Hex {
#[inline]
fn decode<'t, IN: AsRef<[u8]>>(
bin: &'t mut [u8],
b32: IN,
ignore: Option<&[u8]>,
) -> Result<&'t [u8], Error> {
Base32Impl::decode(bin, b32.as_ref(), ignore, Base32Variant::Hex)
}
}
impl Encoder for Base32HexNoPadding {
#[inline]
fn encoded_len(bin_len: usize) -> Result<usize, Error> {
Base32Impl::encoded_len(bin_len, Base32Variant::HexNoPadding)
}
#[inline]
fn encode<IN: AsRef<[u8]>>(b32: &mut [u8], bin: IN) -> Result<&[u8], Error> {
Base32Impl::encode(b32, bin.as_ref(), Base32Variant::HexNoPadding)
}
}
impl Decoder for Base32HexNoPadding {
#[inline]
fn decode<'t, IN: AsRef<[u8]>>(
bin: &'t mut [u8],
b32: IN,
ignore: Option<&[u8]>,
) -> Result<&'t [u8], Error> {
Base32Impl::decode(bin, b32.as_ref(), ignore, Base32Variant::HexNoPadding)
}
}
#[cfg(feature = "std")]
#[test]
fn test_base32() {
let bin = b"Hello";
let expected = "JBSWY3DP";
let b32 = Base32::encode_to_string(bin).unwrap();
assert_eq!(b32, expected);
let padded = "JBSWY3DP======";
let bin2 = Base32::decode_to_vec(padded, None).unwrap();
assert_eq!(bin, &bin2[..]);
}
#[cfg(feature = "std")]
#[test]
fn test_base32_no_padding() {
let bin = b"Hello";
let expected = "JBSWY3DP";
let b32 = Base32NoPadding::encode_to_string(bin).unwrap();
assert_eq!(b32, expected);
let bin2 = Base32NoPadding::decode_to_vec(&b32, None).unwrap();
assert_eq!(bin, &bin2[..]);
}
#[cfg(feature = "std")]
#[test]
fn test_base32_hex() {
let bin = b"Hello";
let expected = "91IMOR3F";
let b32 = Base32Hex::encode_to_string(bin).unwrap();
assert_eq!(b32, expected);
let padded = "91IMOR3F======";
let bin2 = Base32Hex::decode_to_vec(padded, None).unwrap();
assert_eq!(bin, &bin2[..]);
}
#[cfg(feature = "std")]
#[test]
fn test_base32_hex_no_padding() {
let bin = b"Hello";
let expected = "91IMOR3F";
let b32 = Base32HexNoPadding::encode_to_string(bin).unwrap();
assert_eq!(b32, expected);
let bin2 = Base32HexNoPadding::decode_to_vec(&b32, None).unwrap();
assert_eq!(bin, &bin2[..]);
}
#[test]
fn test_base32_no_std() {
let bin = b"Hello";
let expected = b"JBSWY3DP";
let mut b32 = [0u8; 16];
let b32 = Base32::encode(&mut b32, bin).unwrap();
assert_eq!(b32, expected);
let padded = b"JBSWY3DP======";
let mut bin2 = [0u8; 5];
let bin2 = Base32::decode(&mut bin2, padded, None).unwrap();
assert_eq!(bin, bin2);
}
#[cfg(feature = "std")]
#[test]
fn test_base32_invalid_padding() {
let valid_padding = "JBSWY3DP======"; assert!(Base32::decode_to_vec(valid_padding, None).is_ok());
let invalid_padding = "JBSWY3DP===";
assert!(Base32::decode_to_vec(invalid_padding, None).is_err());
}